1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 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
115
116
117
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
135
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
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 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 public Iterator getKeys(String key)
229 {
230 List keys = new ArrayList();
231 for (Iterator i = configList.iterator(); i.hasNext();)
232 {
233 Configuration config = (Configuration) i.next();
234
235 Iterator j = config.getKeys(key);
236 while (j.hasNext())
237 {
238 String newKey = (String) j.next();
239 if (!keys.contains(newKey))
240 {
241 keys.add(newKey);
242 }
243 }
244 }
245
246 return keys.iterator();
247 }
248
249 public boolean isEmpty()
250 {
251 boolean isEmpty = true;
252 for (Iterator i = configList.iterator(); i.hasNext();)
253 {
254 Configuration config = (Configuration) i.next();
255 if (!config.isEmpty())
256 {
257 return false;
258 }
259 }
260
261 return isEmpty;
262 }
263
264 protected void clearPropertyDirect(String key)
265 {
266 for (Iterator i = configList.iterator(); i.hasNext();)
267 {
268 Configuration config = (Configuration) i.next();
269 config.clearProperty(key);
270 }
271 }
272
273 public boolean containsKey(String key)
274 {
275 for (Iterator i = configList.iterator(); i.hasNext();)
276 {
277 Configuration config = (Configuration) i.next();
278 if (config.containsKey(key))
279 {
280 return true;
281 }
282 }
283 return false;
284 }
285
286 public List getList(String key, List defaultValue)
287 {
288 List list = new ArrayList();
289
290
291 Iterator it = configList.iterator();
292 while (it.hasNext() && list.isEmpty())
293 {
294 Configuration config = (Configuration) it.next();
295 if (config != inMemoryConfiguration && config.containsKey(key))
296 {
297 appendListProperty(list, config, key);
298 }
299 }
300
301
302 appendListProperty(list, inMemoryConfiguration, key);
303
304 if (list.isEmpty())
305 {
306 return defaultValue;
307 }
308
309 ListIterator lit = list.listIterator();
310 while (lit.hasNext())
311 {
312 lit.set(interpolate(lit.next()));
313 }
314
315 return list;
316 }
317
318 public String[] getStringArray(String key)
319 {
320 List list = getList(key);
321
322
323 String[] tokens = new String[list.size()];
324
325 for (int i = 0; i < tokens.length; i++)
326 {
327 tokens[i] = String.valueOf(list.get(i));
328 }
329
330 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 return (Configuration) configList.get(index);
342 }
343
344 /***
345 * Returns the "in memory configuration". In this configuration
346 * changes are stored.
347 *
348 * @return the in memory configuration
349 */
350 public Configuration getInMemoryConfiguration()
351 {
352 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 CompositeConfiguration copy = (CompositeConfiguration) super
370 .clone();
371 copy.clearConfigurationListeners();
372 copy.configList = new LinkedList();
373 copy.inMemoryConfiguration = ConfigurationUtils
374 .cloneConfiguration(getInMemoryConfiguration());
375 copy.configList.add(copy.inMemoryConfiguration);
376
377 for (int i = 0; i < getNumberOfConfigurations(); i++)
378 {
379 Configuration config = getConfiguration(i);
380 if (config != getInMemoryConfiguration())
381 {
382 copy.addConfiguration(ConfigurationUtils
383 .cloneConfiguration(config));
384 }
385 }
386
387 return copy;
388 }
389 catch (CloneNotSupportedException cnex)
390 {
391
392 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 ((BaseConfiguration) getInMemoryConfiguration())
407 .setDelimiterParsingDisabled(delimiterParsingDisabled);
408 super.setDelimiterParsingDisabled(delimiterParsingDisabled);
409 }
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 ((BaseConfiguration) getInMemoryConfiguration())
421 .setListDelimiter(listDelimiter);
422 super.setListDelimiter(listDelimiter);
423 }
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 if (key == null)
451 {
452 throw new IllegalArgumentException("Key must not be null!");
453 }
454
455 Configuration source = null;
456 for (Iterator it = configList.iterator(); it.hasNext();)
457 {
458 Configuration conf = (Configuration) it.next();
459 if (conf.containsKey(key))
460 {
461 if (source != null)
462 {
463 throw new IllegalArgumentException("The key " + key
464 + " is defined by multiple sources!");
465 }
466 source = conf;
467 }
468 }
469
470 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 Object value = config.getProperty(key);
486 if (value != null)
487 {
488 if (value instanceof Collection)
489 {
490 dest.addAll((Collection) value);
491 }
492 else
493 {
494 dest.add(value);
495 }
496 }
497 }
498 }