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
9
* http://www.apache.org/licenses/LICENSE-2.0
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.
18
package org.apache.commons.configuration;
20
import java.util.ArrayList;
21
import java.util.Collection;
22
import java.util.Iterator;
23
import java.util.LinkedHashSet;
24
import java.util.LinkedList;
25
import java.util.List;
26
import java.util.ListIterator;
30
* <p>{@code CompositeConfiguration} allows you to add multiple {@code Configuration}
31
* objects to an aggregated configuration. If you add Configuration1, and then Configuration2,
32
* any properties shared will mean that the value defined by Configuration1
33
* will be returned. If Configuration1 doesn't have the property, then
34
* Configuration2 will be checked. You can add multiple different types or the
35
* same type of properties file.</p>
36
* <p>When querying properties the order in which child configurations have been
37
* added is relevant. To deal with property updates, a so-called <em>in-memory
38
* configuration</em> is used. Per default, such a configuration is created
39
* automatically. All property writes target this special configuration. There
40
* are constructors which allow you to provide a specific in-memory configuration.
41
* If used that way, the in-memory configuration is always the last one in the
42
* list of child configurations. This means that for query operations all other
43
* configurations take precedence.</p>
44
* <p>Alternatively it is possible to mark a child configuration as in-memory
45
* configuration when it is added. In this case the treatment of the in-memory
46
* configuration is slightly different: it remains in the list of child
47
* configurations at the position it was added, i.e. its priority for property
48
* queries can be defined by adding the child configurations in the correct
51
* @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
52
* @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
53
* @version $Id: CompositeConfiguration.java 1233058 2012-01-18 20:49:12Z oheger $
55
public class CompositeConfiguration extends AbstractConfiguration
58
/** List holding all the configuration */
59
private List<Configuration> configList = new LinkedList<Configuration>();
62
* Configuration that holds in memory stuff. Inserted as first so any
63
* setProperty() override anything else added.
65
private Configuration inMemoryConfiguration;
68
* Stores a flag whether the current in-memory configuration is also a
69
* child configuration.
71
private boolean inMemoryConfigIsChild;
74
* Creates an empty CompositeConfiguration object which can then
75
* be added some other Configuration files
77
public CompositeConfiguration()
83
* Creates a CompositeConfiguration object with a specified <em>in-memory
84
* configuration</em>. This configuration will store any changes made to the
85
* {@code CompositeConfiguration}. Note: Use this constructor if you want to
86
* set a special type of in-memory configuration. If you have a
87
* configuration which should act as both a child configuration and as
88
* in-memory configuration, use
89
* {@link #addConfiguration(Configuration, boolean)} with a value of
90
* <b>true</b> instead.
92
* @param inMemoryConfiguration the in memory configuration to use
94
public CompositeConfiguration(Configuration inMemoryConfiguration)
97
this.inMemoryConfiguration = inMemoryConfiguration;
98
configList.add(inMemoryConfiguration);
102
* Create a CompositeConfiguration with an empty in memory configuration
103
* and adds the collection of configurations specified.
105
* @param configurations the collection of configurations to add
107
public CompositeConfiguration(Collection<? extends Configuration> configurations)
109
this(new BaseConfiguration(), configurations);
113
* Creates a CompositeConfiguration with a specified <em>in-memory
114
* configuration</em>, and then adds the given collection of configurations.
116
* @param inMemoryConfiguration the in memory configuration to use
117
* @param configurations the collection of configurations to add
118
* @see #CompositeConfiguration(Configuration)
120
public CompositeConfiguration(Configuration inMemoryConfiguration,
121
Collection<? extends Configuration> configurations)
123
this(inMemoryConfiguration);
125
if (configurations != null)
127
for (Configuration c : configurations)
135
* Add a configuration.
137
* @param config the configuration to add
139
public void addConfiguration(Configuration config)
141
addConfiguration(config, false);
145
* Adds a child configuration and optionally makes it the <em>in-memory
146
* configuration</em>. This means that all future property write operations
147
* are executed on this configuration. Note that the current in-memory
148
* configuration is replaced by the new one. If it was created automatically
149
* or passed to the constructor, it is removed from the list of child
150
* configurations! Otherwise, it stays in the list of child configurations
151
* at its current position, but it passes its role as in-memory
152
* configuration to the new one.
154
* @param config the configuration to be added
155
* @param asInMemory <b>true</b> if this configuration becomes the new
156
* <em>in-memory</em> configuration, <b>false</b> otherwise
159
public void addConfiguration(Configuration config, boolean asInMemory)
161
if (!configList.contains(config))
165
replaceInMemoryConfiguration(config);
166
inMemoryConfigIsChild = true;
169
if (!inMemoryConfigIsChild)
171
// As the inMemoryConfiguration contains all manually added
172
// keys, we must make sure that it is always last. "Normal", non
173
// composed configurations add their keys at the end of the
174
// configuration and we want to mimic this behavior.
175
configList.add(configList.indexOf(inMemoryConfiguration),
180
// However, if the in-memory configuration is a regular child,
181
// only the order in which child configurations are added is
183
configList.add(config);
186
if (config instanceof AbstractConfiguration)
188
((AbstractConfiguration) config)
189
.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
195
* Remove a configuration. The in memory configuration cannot be removed.
197
* @param config The configuration to remove
199
public void removeConfiguration(Configuration config)
201
// Make sure that you can't remove the inMemoryConfiguration from
202
// the CompositeConfiguration object
203
if (!config.equals(inMemoryConfiguration))
205
configList.remove(config);
210
* Return the number of configurations.
212
* @return the number of configuration
214
public int getNumberOfConfigurations()
216
return configList.size();
220
* Removes all child configurations and reinitializes the <em>in-memory
221
* configuration</em>. <strong>Attention:</strong> A new in-memory
222
* configuration is created; the old one is lost.
228
// recreate the in memory configuration
229
inMemoryConfiguration = new BaseConfiguration();
230
((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
231
((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
232
((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
233
configList.add(inMemoryConfiguration);
234
inMemoryConfigIsChild = false;
238
* Add this property to the inmemory Configuration.
240
* @param key The Key to add the property to.
241
* @param token The Value to add.
244
protected void addPropertyDirect(String key, Object token)
246
inMemoryConfiguration.addProperty(key, token);
250
* Read property from underlying composite
252
* @param key key to use for mapping
254
* @return object associated with the given configuration key.
256
public Object getProperty(String key)
258
Configuration firstMatchingConfiguration = null;
259
for (Configuration config : configList)
261
if (config.containsKey(key))
263
firstMatchingConfiguration = config;
268
if (firstMatchingConfiguration != null)
270
return firstMatchingConfiguration.getProperty(key);
278
public Iterator<String> getKeys()
280
Set<String> keys = new LinkedHashSet<String>();
281
for (Configuration config : configList)
283
for (Iterator<String> it = config.getKeys(); it.hasNext();)
289
return keys.iterator();
293
public Iterator<String> getKeys(String key)
295
Set<String> keys = new LinkedHashSet<String>();
296
for (Configuration config : configList)
298
for (Iterator<String> it = config.getKeys(key); it.hasNext();)
304
return keys.iterator();
307
public boolean isEmpty()
309
for (Configuration config : configList)
311
if (!config.isEmpty())
321
protected void clearPropertyDirect(String key)
323
for (Configuration config : configList)
325
config.clearProperty(key);
329
public boolean containsKey(String key)
331
for (Configuration config : configList)
333
if (config.containsKey(key))
342
public List<Object> getList(String key, List<Object> defaultValue)
344
List<Object> list = new ArrayList<Object>();
346
// add all elements from the first configuration containing the requested key
347
Iterator<Configuration> it = configList.iterator();
348
while (it.hasNext() && list.isEmpty())
350
Configuration config = it.next();
351
if (config != inMemoryConfiguration && config.containsKey(key))
353
appendListProperty(list, config, key);
357
// add all elements from the in memory configuration
358
appendListProperty(list, inMemoryConfiguration, key);
365
ListIterator<Object> lit = list.listIterator();
366
while (lit.hasNext())
368
lit.set(interpolate(lit.next()));
375
public String[] getStringArray(String key)
377
List<Object> list = getList(key);
379
// transform property values into strings
380
String[] tokens = new String[list.size()];
382
for (int i = 0; i < tokens.length; i++)
384
tokens[i] = String.valueOf(list.get(i));
391
* Return the configuration at the specified index.
393
* @param index The index of the configuration to retrieve
394
* @return the configuration at this index
396
public Configuration getConfiguration(int index)
398
return configList.get(index);
402
* Returns the "in memory configuration". In this configuration
403
* changes are stored.
405
* @return the in memory configuration
407
public Configuration getInMemoryConfiguration()
409
return inMemoryConfiguration;
413
* Returns a copy of this object. This implementation will create a deep
414
* clone, i.e. all configurations contained in this composite will also be
415
* cloned. This only works if all contained configurations support cloning;
416
* otherwise a runtime exception will be thrown. Registered event handlers
423
public Object clone()
427
CompositeConfiguration copy = (CompositeConfiguration) super
429
copy.clearConfigurationListeners();
430
copy.configList = new LinkedList<Configuration>();
431
copy.inMemoryConfiguration = ConfigurationUtils
432
.cloneConfiguration(getInMemoryConfiguration());
433
copy.configList.add(copy.inMemoryConfiguration);
435
for (Configuration config : configList)
437
if (config != getInMemoryConfiguration())
439
copy.addConfiguration(ConfigurationUtils
440
.cloneConfiguration(config));
446
catch (CloneNotSupportedException cnex)
449
throw new ConfigurationRuntimeException(cnex);
454
* Sets a flag whether added values for string properties should be checked
455
* for the list delimiter. This implementation ensures that the in memory
456
* configuration is correctly initialized.
458
* @param delimiterParsingDisabled the new value of the flag
462
public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
464
if (inMemoryConfiguration instanceof AbstractConfiguration)
466
((AbstractConfiguration) inMemoryConfiguration)
467
.setDelimiterParsingDisabled(delimiterParsingDisabled);
469
super.setDelimiterParsingDisabled(delimiterParsingDisabled);
473
* Sets the character that is used as list delimiter. This implementation
474
* ensures that the in memory configuration is correctly initialized.
476
* @param listDelimiter the new list delimiter character
480
public void setListDelimiter(char listDelimiter)
482
if (inMemoryConfiguration instanceof AbstractConfiguration)
484
((AbstractConfiguration) inMemoryConfiguration)
485
.setListDelimiter(listDelimiter);
487
super.setListDelimiter(listDelimiter);
491
* Returns the configuration source, in which the specified key is defined.
492
* This method will iterate over all existing child configurations and check
493
* whether they contain the specified key. The following constellations are
496
* <li>If exactly one child configuration contains the key, this
497
* configuration is returned as the source configuration. This may be the
498
* <em>in memory configuration</em> (this has to be explicitly checked by
499
* the calling application).</li>
500
* <li>If none of the child configurations contain the key, <b>null</b> is
502
* <li>If the key is contained in multiple child configurations or if the
503
* key is <b>null</b>, a {@code IllegalArgumentException} is thrown.
504
* In this case the source configuration cannot be determined.</li>
507
* @param key the key to be checked
508
* @return the source configuration of this key
509
* @throws IllegalArgumentException if the source configuration cannot be
513
public Configuration getSource(String key)
517
throw new IllegalArgumentException("Key must not be null!");
520
Configuration source = null;
521
for (Configuration conf : configList)
523
if (conf.containsKey(key))
527
throw new IllegalArgumentException("The key " + key
528
+ " is defined by multiple sources!");
538
* Replaces the current in-memory configuration by the given one.
540
* @param config the new in-memory configuration
542
private void replaceInMemoryConfiguration(Configuration config)
544
if (!inMemoryConfigIsChild)
546
// remove current in-memory configuration
547
configList.remove(inMemoryConfiguration);
549
inMemoryConfiguration = config;
553
* Adds the value of a property to the given list. This method is used by
554
* {@code getList()} for gathering property values from the child
557
* @param dest the list for collecting the data
558
* @param config the configuration to query
559
* @param key the key of the property
561
private static void appendListProperty(List<Object> dest, Configuration config,
564
Object value = config.getProperty(key);
567
if (value instanceof Collection)
569
Collection<?> col = (Collection<?>) value;