##### Coverage Report - org.apache.commons.configuration.CompositeConfiguration

Classes in this File | Line Coverage | Branch Coverage | Complexity
CompositeConfiguration |
98% | 128/131
100% | 32/32
| 2.7083333333333335;2,708

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 705028 2008-10-15 20:33:35Z oheger $
37 |  |
      */
38 |  |
     public class CompositeConfiguration extends AbstractConfiguration
39 |  |
     implements Cloneable
40 |  |
     {
41 |  |
         /** List holding all the configuration */
42 | 127 |
         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 | 122 |
         {
56 | 122 |
             clear();
57 | 122 |
         }
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 | 5 |
         {
68 | 5 |
             configList.clear();
69 | 5 |
             this.inMemoryConfiguration = inMemoryConfiguration;
70 | 5 |
             configList.add(inMemoryConfiguration);
71 | 5 |
         }
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 | 1 |
             this(new BaseConfiguration(), configurations);
82 | 1 |
         }
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 | 1 |
             this(inMemoryConfiguration);
94 |  |
95 | 1 |
             if (configurations != null)
96 |  |
             {
97 | 1 |
                 Iterator it = configurations.iterator();
98 | 4 |
                 while (it.hasNext())
99 |  |
                 {
100 | 3 |
                     addConfiguration((Configuration) it.next());
101 |  |
                 }
102 |  |
             }
103 | 1 |
         }
104 |  |
105 |  |
         /**
106 |  |
          * Add a configuration.
107 |  |
          *
108 |  |
          * @param config the configuration to add
109 |  |
          */
110 |  |
         public void addConfiguration(Configuration config)
111 |  |
         {
112 | 168 |
             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 | 166 |
                 configList.add(configList.indexOf(inMemoryConfiguration), config);
119 |  |
120 | 166 |
                 if (config instanceof AbstractConfiguration)
121 |  |
                 {
122 | 166 |
                     ((AbstractConfiguration) config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
123 |  |
                 }
124 |  |
             }
125 | 168 |
         }
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 | 4 |
             if (!config.equals(inMemoryConfiguration))
137 |  |
             {
138 | 2 |
                 configList.remove(config);
139 |  |
             }
140 | 4 |
         }
141 |  |
142 |  |
         /**
143 |  |
          * Return the number of configurations.
144 |  |
          *
145 |  |
          * @return the number of configuration
146 |  |
          */
147 |  |
         public int getNumberOfConfigurations()
148 |  |
         {
149 | 31 |
             return configList.size();
150 |  |
         }
151 |  |
152 |  |
         /**
153 |  |
          * Remove all configuration reinitialize the in memory configuration.
154 |  |
          */
155 |  |
         public void clear()
156 |  |
         {
157 | 132 |
             configList.clear();
158 |  |
             // recreate the in memory configuration
159 | 132 |
             inMemoryConfiguration = new BaseConfiguration();
160 | 132 |
             ((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
161 | 132 |
             ((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
162 | 132 |
             ((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
163 | 132 |
             configList.add(inMemoryConfiguration);
164 | 132 |
         }
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 | 35 |
             inMemoryConfiguration.addProperty(key, token);
175 | 35 |
         }
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 | 142 |
             Configuration firstMatchingConfiguration = null;
187 | 142 |
             for (Iterator i = configList.iterator(); i.hasNext();)
188 |  |
             {
189 | 212 |
                 Configuration config = (Configuration) i.next();
190 | 212 |
                 if (config.containsKey(key))
191 |  |
                 {
192 | 117 |
                     firstMatchingConfiguration = config;
193 | 117 |
                     break;
194 |  |
                 }
195 |  |
             }
196 |  |
197 | 142 |
             if (firstMatchingConfiguration != null)
198 |  |
             {
199 | 117 |
                 return firstMatchingConfiguration.getProperty(key);
200 |  |
             }
201 |  |
             else
202 |  |
             {
203 | 25 |
                 return null;
204 |  |
             }
205 |  |
         }
206 |  |
207 |  |
         public Iterator getKeys()
208 |  |
         {
209 | 14 |
             List keys = new ArrayList();
210 | 14 |
             for (Iterator i = configList.iterator(); i.hasNext();)
211 |  |
             {
212 | 24 |
                 Configuration config = (Configuration) i.next();
213 |  |
214 | 24 |
                 Iterator j = config.getKeys();
215 | 154 |
                 while (j.hasNext())
216 |  |
                 {
217 | 130 |
                     String key = (String) j.next();
218 | 130 |
                     if (!keys.contains(key))
219 |  |
                     {
220 | 129 |
                         keys.add(key);
221 |  |
                     }
222 |  |
                 }
223 |  |
             }
224 |  |
225 | 14 |
             return keys.iterator();
226 |  |
         }
227 |  |
228 |  |
         public Iterator getKeys(String key)
229 |  |
         {
230 | 9 |
             List keys = new ArrayList();
231 | 9 |
             for (Iterator i = configList.iterator(); i.hasNext();)
232 |  |
             {
233 | 22 |
                 Configuration config = (Configuration) i.next();
234 |  |
235 | 22 |
                 Iterator j = config.getKeys(key);
236 | 218 |
                 while (j.hasNext())
237 |  |
                 {
238 | 196 |
                     String newKey = (String) j.next();
239 | 196 |
                     if (!keys.contains(newKey))
240 |  |
                     {
241 | 194 |
                         keys.add(newKey);
242 |  |
                     }
243 |  |
                 }
244 |  |
             }
245 |  |
246 | 9 |
             return keys.iterator();
247 |  |
         }
248 |  |
249 |  |
         public boolean isEmpty()
250 |  |
         {
251 | 4 |
             boolean isEmpty = true;
252 | 4 |
             for (Iterator i = configList.iterator(); i.hasNext();)
253 |  |
             {
254 | 4 |
                 Configuration config = (Configuration) i.next();
255 | 4 |
                 if (!config.isEmpty())
256 |  |
                 {
257 | 4 |
                     return false;
258 |  |
                 }
259 |  |
             }
260 |  |
261 | 0 |
             return isEmpty;
262 |  |
         }
263 |  |
264 |  |
         protected void clearPropertyDirect(String key)
265 |  |
         {
266 | 16 |
             for (Iterator i = configList.iterator(); i.hasNext();)
267 |  |
             {
268 | 35 |
                 Configuration config = (Configuration) i.next();
269 | 35 |
                 config.clearProperty(key);
270 |  |
             }
271 | 16 |
         }
272 |  |
273 |  |
         public boolean containsKey(String key)
274 |  |
         {
275 | 55 |
             for (Iterator i = configList.iterator(); i.hasNext();)
276 |  |
             {
277 | 72 |
                 Configuration config = (Configuration) i.next();
278 | 72 |
                 if (config.containsKey(key))
279 |  |
                 {
280 | 44 |
                     return true;
281 |  |
                 }
282 |  |
             }
283 | 11 |
             return false;
284 |  |
         }
285 |  |
286 |  |
         public List getList(String key, List defaultValue)
287 |  |
         {
288 | 48 |
             List list = new ArrayList();
289 |  |
290 |  |
             // add all elements from the first configuration containing the requested key
291 | 48 |
             Iterator it = configList.iterator();
292 | 113 |
             while (it.hasNext() && list.isEmpty())
293 |  |
             {
294 | 65 |
                 Configuration config = (Configuration) it.next();
295 | 65 |
                 if (config != inMemoryConfiguration && config.containsKey(key))
296 |  |
                 {
297 | 34 |
                     appendListProperty(list, config, key);
298 |  |
                 }
299 |  |
             }
300 |  |
301 |  |
             // add all elements from the in memory configuration
302 | 48 |
             appendListProperty(list, inMemoryConfiguration, key);
303 |  |
304 | 48 |
             if (list.isEmpty())
305 |  |
             {
306 | 5 |
                 return defaultValue;
307 |  |
             }
308 |  |
309 | 43 |
             ListIterator lit = list.listIterator();
310 | 124 |
             while (lit.hasNext())
311 |  |
             {
312 | 81 |
                 lit.set(interpolate(lit.next()));
313 |  |
             }
314 |  |
315 | 43 |
             return list;
316 |  |
         }
317 |  |
318 |  |
         public String[] getStringArray(String key)
319 |  |
         {
320 | 14 |
             List list = getList(key);
321 |  |
322 |  |
             // transform property values into strings
323 | 14 |
             String[] tokens = new String[list.size()];
324 |  |
325 | 35 |
             for (int i = 0; i < tokens.length; i++)
326 |  |
             {
327 | 21 |
                 tokens[i] = String.valueOf(list.get(i));
328 |  |
             }
329 |  |
330 | 14 |
             return tokens;
331 |  |
         }
332 |  |
333 |  |
         /**
334 |  |
          * Return the configuration at the specified index.
335 |  |
          *
336 |  |
          * @param index The index of the configuration to retrieve
337 |  |
          * @return the configuration at this index
338 |  |
          */
339 |  |
         public Configuration getConfiguration(int index)
340 |  |
         {
341 | 29 |
             return (Configuration) configList.get(index);
342 |  |
         }
343 |  |
344 |  |
         /**
345 |  |
          * Returns the &quot;in memory configuration&quot;. In this configuration
346 |  |
          * changes are stored.
347 |  |
          *
348 |  |
          * @return the in memory configuration
349 |  |
          */
350 |  |
         public Configuration getInMemoryConfiguration()
351 |  |
         {
352 | 12 |
             return inMemoryConfiguration;
353 |  |
         }
354 |  |
355 |  |
         /**
356 |  |
          * Returns a copy of this object. This implementation will create a deep
357 |  |
          * clone, i.e. all configurations contained in this composite will also be
358 |  |
          * cloned. This only works if all contained configurations support cloning;
359 |  |
          * otherwise a runtime exception will be thrown. Registered event handlers
360 |  |
          * won't get cloned.
361 |  |
          *
362 |  |
          * @return the copy
363 |  |
          * @since 1.3
364 |  |
          */
365 |  |
         public Object clone()
366 |  |
         {
367 |  |
             try
368 |  |
             {
369 | 3 |
                 CompositeConfiguration copy = (CompositeConfiguration) super
370 |  |
                         .clone();
371 | 3 |
                 copy.clearConfigurationListeners();
372 | 3 |
                 copy.configList = new LinkedList();
373 | 3 |
                 copy.inMemoryConfiguration = ConfigurationUtils
374 |  |
                         .cloneConfiguration(getInMemoryConfiguration());
375 | 3 |
                 copy.configList.add(copy.inMemoryConfiguration);
376 |  |
377 | 5 |
                 for (int i = 0; i < getNumberOfConfigurations(); i++)
378 |  |
                 {
379 | 3 |
                     Configuration config = getConfiguration(i);
380 | 3 |
                     if (config != getInMemoryConfiguration())
381 |  |
                     {
382 | 1 |
                         copy.addConfiguration(ConfigurationUtils
383 |  |
                                 .cloneConfiguration(config));
384 |  |
                     }
385 |  |
                 }
386 |  |
387 | 2 |
                 return copy;
388 |  |
             }
389 | 0 |
             catch (CloneNotSupportedException cnex)
390 |  |
             {
391 |  |
                 // cannot happen
392 | 0 |
                 throw new ConfigurationRuntimeException(cnex);
393 |  |
             }
394 |  |
         }
395 |  |
396 |  |
         /**
397 |  |
          * Sets a flag whether added values for string properties should be checked
398 |  |
          * for the list delimiter. This implementation ensures that the in memory
399 |  |
          * configuration is correctly initialized.
400 |  |
          *
401 |  |
          * @param delimiterParsingDisabled the new value of the flag
402 |  |
          * @since 1.4
403 |  |
          */
404 |  |
         public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
405 |  |
         {
406 | 2 |
             ((BaseConfiguration) getInMemoryConfiguration())
407 |  |
                     .setDelimiterParsingDisabled(delimiterParsingDisabled);
408 | 2 |
             super.setDelimiterParsingDisabled(delimiterParsingDisabled);
409 | 2 |
         }
410 |  |
411 |  |
         /**
412 |  |
          * Sets the character that is used as list delimiter. This implementation
413 |  |
          * ensures that the in memory configuration is correctly initialized.
414 |  |
          *
415 |  |
          * @param listDelimiter the new list delimiter character
416 |  |
          * @since 1.4
417 |  |
          */
418 |  |
         public void setListDelimiter(char listDelimiter)
419 |  |
         {
420 | 2 |
             ((BaseConfiguration) getInMemoryConfiguration())
421 |  |
                     .setListDelimiter(listDelimiter);
422 | 2 |
             super.setListDelimiter(listDelimiter);
423 | 2 |
         }
424 |  |
425 |  |
         /**
426 |  |
          * Returns the configuration source, in which the specified key is defined.
427 |  |
          * This method will iterate over all existing child configurations and check
428 |  |
          * whether they contain the specified key. The following constellations are
429 |  |
          * possible:
430 |  |
          * <ul>
431 |  |
          * <li>If exactly one child configuration contains the key, this
432 |  |
          * configuration is returned as the source configuration. This may be the
433 |  |
          * <em>in memory configuration</em> (this has to be explicitly checked by
434 |  |
          * the calling application).</li>
435 |  |
          * <li>If none of the child configurations contain the key, <b>null</b> is
436 |  |
          * returned.</li>
437 |  |
          * <li>If the key is contained in multiple child configurations or if the
438 |  |
          * key is <b>null</b>, a <code>IllegalArgumentException</code> is thrown.
439 |  |
          * In this case the source configuration cannot be determined.</li>
440 |  |
          * </ul>
441 |  |
          *
442 |  |
          * @param key the key to be checked
443 |  |
          * @return the source configuration of this key
444 |  |
          * @throws IllegalArgumentException if the source configuration cannot be
445 |  |
          * determined
446 |  |
          * @since 1.5
447 |  |
          */
448 |  |
         public Configuration getSource(String key)
449 |  |
         {
450 | 5 |
             if (key == null)
451 |  |
             {
452 | 1 |
                 throw new IllegalArgumentException("Key must not be null!");
453 |  |
             }
454 |  |
455 | 4 |
             Configuration source = null;
456 | 4 |
             for (Iterator it = configList.iterator(); it.hasNext();)
457 |  |
             {
458 | 12 |
                 Configuration conf = (Configuration) it.next();
459 | 12 |
                 if (conf.containsKey(key))
460 |  |
                 {
461 | 4 |
                     if (source != null)
462 |  |
                     {
463 | 1 |
                         throw new IllegalArgumentException("The key " + key
464 |  |
                                 + " is defined by multiple sources!");
465 |  |
                     }
466 | 3 |
                     source = conf;
467 |  |
                 }
468 |  |
             }
469 |  |
470 | 3 |
             return source;
471 |  |
         }
472 |  |
473 |  |
         /**
474 |  |
          * Adds the value of a property to the given list. This method is used by
475 |  |
          * <code>getList()</code> for gathering property values from the child
476 |  |
          * configurations.
477 |  |
          *
478 |  |
          * @param dest the list for collecting the data
479 |  |
          * @param config the configuration to query
480 |  |
          * @param key the key of the property
481 |  |
          */
482 |  |
         private static void appendListProperty(List dest, Configuration config,
483 |  |
                 String key)
484 |  |
         {
485 | 82 |
             Object value = config.getProperty(key);
486 | 82 |
             if (value != null)
487 |  |
             {
488 | 50 |
                 if (value instanceof Collection)
489 |  |
                 {
490 | 22 |
                     dest.addAll((Collection) value);
491 |  |
                 }
492 |  |
                 else
493 |  |
                 {
494 | 28 |
                     dest.add(value);
495 |  |
                 }
496 |  |
             }
497 | 82 |
         }
498 |  |
     }