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