View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration;
19  
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.ListIterator;
26  
27  /***
28   * This Configuration class allows you to add multiple different types of Configuration
29   * to this CompositeConfiguration.  If you add Configuration1, and then Configuration2,
30   * any properties shared will mean that Configuration1 will be returned.
31   * You can add multiple different types or the same type of properties file.
32   * If Configuration1 doesn't have the property, then Configuration2 will be checked.
33   *
34   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
35   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
36   * @version $Id: CompositeConfiguration.java 494581 2007-01-09 21:14:20Z oheger $
37   */
38  public class CompositeConfiguration extends AbstractConfiguration
39  implements Cloneable
40  {
41      /*** List holding all the configuration */
42      private List configList = new LinkedList();
43  
44      /***
45       * Configuration that holds in memory stuff.  Inserted as first so any
46       * setProperty() override anything else added.
47       */
48      private Configuration inMemoryConfiguration;
49  
50      /***
51       * Creates an empty CompositeConfiguration object which can then
52       * be added some other Configuration files
53       */
54      public CompositeConfiguration()
55      {
56          clear();
57      }
58  
59      /***
60       * Creates a CompositeConfiguration object with a specified in memory
61       * configuration. This configuration will store any changes made to
62       * the CompositeConfiguration.
63       *
64       * @param inMemoryConfiguration the in memory configuration to use
65       */
66      public CompositeConfiguration(Configuration inMemoryConfiguration)
67      {
68          configList.clear();
69          this.inMemoryConfiguration = inMemoryConfiguration;
70          configList.add(inMemoryConfiguration);
71      }
72  
73      /***
74       * Create a CompositeConfiguration with an empty in memory configuration
75       * and adds the collection of configurations specified.
76       *
77       * @param configurations the collection of configurations to add
78       */
79      public CompositeConfiguration(Collection configurations)
80      {
81          this(new BaseConfiguration(), configurations);
82      }
83  
84      /***
85       * Creates a CompositeConfiguration with a specified in memory
86       * configuration, and then adds the given collection of configurations.
87       *
88       * @param inMemoryConfiguration the in memory configuration to use
89       * @param configurations        the collection of configurations to add
90       */
91      public CompositeConfiguration(Configuration inMemoryConfiguration, Collection configurations)
92      {
93          this(inMemoryConfiguration);
94  
95          if (configurations != null)
96          {
97              Iterator it = configurations.iterator();
98              while (it.hasNext())
99              {
100                 addConfiguration((Configuration) it.next());
101             }
102         }
103     }
104 
105     /***
106      * Add a configuration.
107      *
108      * @param config the configuration to add
109      */
110     public void addConfiguration(Configuration config)
111     {
112         if (!configList.contains(config))
113         {
114             // As the inMemoryConfiguration contains all manually added keys,
115             // we must make sure that it is always last. "Normal", non composed
116             // configuration add their keys at the end of the configuration and
117             // we want to mimic this behaviour.
118             configList.add(configList.indexOf(inMemoryConfiguration), config);
119 
120             if (config instanceof AbstractConfiguration)
121             {
122                 ((AbstractConfiguration) config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
123             }
124         }
125     }
126 
127     /***
128      * Remove a configuration. The in memory configuration cannot be removed.
129      *
130      * @param config The configuration to remove
131      */
132     public void removeConfiguration(Configuration config)
133     {
134         // Make sure that you can't remove the inMemoryConfiguration from
135         // the CompositeConfiguration object
136         if (!config.equals(inMemoryConfiguration))
137         {
138             configList.remove(config);
139         }
140     }
141 
142     /***
143      * Return the number of configurations.
144      *
145      * @return the number of configuration
146      */
147     public int getNumberOfConfigurations()
148     {
149         return configList.size();
150     }
151 
152     /***
153      * Remove all configuration reinitialize the in memory configuration.
154      */
155     public void clear()
156     {
157         configList.clear();
158         // recreate the in memory configuration
159         inMemoryConfiguration = new BaseConfiguration();
160         ((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
161         ((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
162         ((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
163         configList.add(inMemoryConfiguration);
164     }
165 
166     /***
167      * Add this property to the inmemory Configuration.
168      *
169      * @param key The Key to add the property to.
170      * @param token The Value to add.
171      */
172     protected void addPropertyDirect(String key, Object token)
173     {
174         inMemoryConfiguration.addProperty(key, token);
175     }
176 
177     /***
178      * Read property from underlying composite
179      *
180      * @param key key to use for mapping
181      *
182      * @return object associated with the given configuration key.
183      */
184     public Object getProperty(String key)
185     {
186         Configuration firstMatchingConfiguration = null;
187         for (Iterator i = configList.iterator(); i.hasNext();)
188         {
189             Configuration config = (Configuration) i.next();
190             if (config.containsKey(key))
191             {
192                 firstMatchingConfiguration = config;
193                 break;
194             }
195         }
196 
197         if (firstMatchingConfiguration != null)
198         {
199             return firstMatchingConfiguration.getProperty(key);
200         }
201         else
202         {
203             return null;
204         }
205     }
206 
207     /***
208      * {@inheritDoc}
209      */
210     public Iterator getKeys()
211     {
212         List keys = new ArrayList();
213         for (Iterator i = configList.iterator(); i.hasNext();)
214         {
215             Configuration config = (Configuration) i.next();
216 
217             Iterator j = config.getKeys();
218             while (j.hasNext())
219             {
220                 String key = (String) j.next();
221                 if (!keys.contains(key))
222                 {
223                     keys.add(key);
224                 }
225             }
226         }
227 
228         return keys.iterator();
229     }
230 
231     /***
232      * {@inheritDoc}
233      */
234     public Iterator getKeys(String key)
235     {
236         List keys = new ArrayList();
237         for (Iterator i = configList.iterator(); i.hasNext();)
238         {
239             Configuration config = (Configuration) i.next();
240 
241             Iterator j = config.getKeys(key);
242             while (j.hasNext())
243             {
244                 String newKey = (String) j.next();
245                 if (!keys.contains(newKey))
246                 {
247                     keys.add(newKey);
248                 }
249             }
250         }
251 
252         return keys.iterator();
253     }
254 
255     /***
256      * {@inheritDoc}
257      */
258     public boolean isEmpty()
259     {
260         boolean isEmpty = true;
261         for (Iterator i = configList.iterator(); i.hasNext();)
262         {
263             Configuration config = (Configuration) i.next();
264             if (!config.isEmpty())
265             {
266                 return false;
267             }
268         }
269 
270         return isEmpty;
271     }
272 
273     /***
274      * {@inheritDoc}
275      */
276     protected void clearPropertyDirect(String key)
277     {
278         for (Iterator i = configList.iterator(); i.hasNext();)
279         {
280             Configuration config = (Configuration) i.next();
281             config.clearProperty(key);
282         }
283     }
284 
285     /***
286      * {@inheritDoc}
287      */
288     public boolean containsKey(String key)
289     {
290         for (Iterator i = configList.iterator(); i.hasNext();)
291         {
292             Configuration config = (Configuration) i.next();
293             if (config.containsKey(key))
294             {
295                 return true;
296             }
297         }
298         return false;
299     }
300 
301     /***
302      * {@inheritDoc}
303      */
304     public List getList(String key, List defaultValue)
305     {
306         List list = new ArrayList();
307 
308         // add all elements from the first configuration containing the requested key
309         Iterator it = configList.iterator();
310         while (it.hasNext() && list.isEmpty())
311         {
312             Configuration config = (Configuration) it.next();
313             if (config != inMemoryConfiguration && config.containsKey(key))
314             {
315                 list.addAll(config.getList(key));
316             }
317         }
318 
319         // add all elements from the in memory configuration
320         list.addAll(inMemoryConfiguration.getList(key));
321 
322         if (list.isEmpty())
323         {
324             return defaultValue;
325         }
326 
327         ListIterator lit = list.listIterator();
328         while (lit.hasNext())
329         {
330             lit.set(interpolate(lit.next()));
331         }
332 
333         return list;
334     }
335 
336     /***
337      * {@inheritDoc}
338      */
339     public String[] getStringArray(String key)
340     {
341         List list = getList(key);
342 
343         // interpolate the strings
344         String[] tokens = new String[list.size()];
345 
346         for (int i = 0; i < tokens.length; i++)
347         {
348             tokens[i] = interpolate(String.valueOf(list.get(i)));
349         }
350 
351         return tokens;
352     }
353 
354     /***
355      * Return the configuration at the specified index.
356      *
357      * @param index The index of the configuration to retrieve
358      * @return the configuration at this index
359      */
360     public Configuration getConfiguration(int index)
361     {
362         return (Configuration) configList.get(index);
363     }
364 
365     /***
366      * Returns the &quot;in memory configuration&quot;. In this configuration
367      * changes are stored.
368      *
369      * @return the in memory configuration
370      */
371     public Configuration getInMemoryConfiguration()
372     {
373         return inMemoryConfiguration;
374     }
375 
376     /***
377      * Returns a copy of this object. This implementation will create a deep
378      * clone, i.e. all configurations contained in this composite will also be
379      * cloned. This only works if all contained configurations support cloning;
380      * otherwise a runtime exception will be thrown. Registered event handlers
381      * won't get cloned.
382      *
383      * @return the copy
384      * @since 1.3
385      */
386     public Object clone()
387     {
388         try
389         {
390             CompositeConfiguration copy = (CompositeConfiguration) super
391                     .clone();
392             copy.clearConfigurationListeners();
393             copy.configList = new LinkedList();
394             copy.inMemoryConfiguration = ConfigurationUtils
395                     .cloneConfiguration(getInMemoryConfiguration());
396             copy.configList.add(copy.inMemoryConfiguration);
397 
398             for (int i = 0; i < getNumberOfConfigurations(); i++)
399             {
400                 Configuration config = getConfiguration(i);
401                 if (config != getInMemoryConfiguration())
402                 {
403                     copy.addConfiguration(ConfigurationUtils
404                             .cloneConfiguration(config));
405                 }
406             }
407 
408             return copy;
409         }
410         catch (CloneNotSupportedException cnex)
411         {
412             // cannot happen
413             throw new ConfigurationRuntimeException(cnex);
414         }
415     }
416 
417     /***
418      * Sets a flag whether added values for string properties should be checked
419      * for the list delimiter. This implementation ensures that the in memory
420      * configuration is correctly initialized.
421      *
422      * @param delimiterParsingDisabled the new value of the flag
423      * @since 1.4
424      */
425     public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
426     {
427         ((BaseConfiguration) getInMemoryConfiguration())
428                 .setDelimiterParsingDisabled(delimiterParsingDisabled);
429         super.setDelimiterParsingDisabled(delimiterParsingDisabled);
430     }
431 
432     /***
433      * Sets the character that is used as list delimiter. This implementation
434      * ensures that the in memory configuration is correctly initialized.
435      *
436      * @param listDelimiter the new list delimiter character
437      * @since 1.4
438      */
439     public void setListDelimiter(char listDelimiter)
440     {
441         ((BaseConfiguration) getInMemoryConfiguration())
442                 .setListDelimiter(listDelimiter);
443         super.setListDelimiter(listDelimiter);
444     }
445 }