001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.configuration;
019:
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.LinkedList;
024: import java.util.List;
025: import java.util.ListIterator;
026:
027: /**
028: * This Configuration class allows you to add multiple different types of Configuration
029: * to this CompositeConfiguration. If you add Configuration1, and then Configuration2,
030: * any properties shared will mean that Configuration1 will be returned.
031: * You can add multiple different types or the same type of properties file.
032: * If Configuration1 doesn't have the property, then Configuration2 will be checked.
033: *
034: * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
035: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
036: * @version $Id: CompositeConfiguration.java 494581 2007-01-09 21:14:20Z oheger $
037: */
038: public class CompositeConfiguration extends AbstractConfiguration
039: implements Cloneable {
040: /** List holding all the configuration */
041: private List configList = new LinkedList();
042:
043: /**
044: * Configuration that holds in memory stuff. Inserted as first so any
045: * setProperty() override anything else added.
046: */
047: private Configuration inMemoryConfiguration;
048:
049: /**
050: * Creates an empty CompositeConfiguration object which can then
051: * be added some other Configuration files
052: */
053: public CompositeConfiguration() {
054: clear();
055: }
056:
057: /**
058: * Creates a CompositeConfiguration object with a specified in memory
059: * configuration. This configuration will store any changes made to
060: * the CompositeConfiguration.
061: *
062: * @param inMemoryConfiguration the in memory configuration to use
063: */
064: public CompositeConfiguration(Configuration inMemoryConfiguration) {
065: configList.clear();
066: this .inMemoryConfiguration = inMemoryConfiguration;
067: configList.add(inMemoryConfiguration);
068: }
069:
070: /**
071: * Create a CompositeConfiguration with an empty in memory configuration
072: * and adds the collection of configurations specified.
073: *
074: * @param configurations the collection of configurations to add
075: */
076: public CompositeConfiguration(Collection configurations) {
077: this (new BaseConfiguration(), configurations);
078: }
079:
080: /**
081: * Creates a CompositeConfiguration with a specified in memory
082: * configuration, and then adds the given collection of configurations.
083: *
084: * @param inMemoryConfiguration the in memory configuration to use
085: * @param configurations the collection of configurations to add
086: */
087: public CompositeConfiguration(Configuration inMemoryConfiguration,
088: Collection configurations) {
089: this (inMemoryConfiguration);
090:
091: if (configurations != null) {
092: Iterator it = configurations.iterator();
093: while (it.hasNext()) {
094: addConfiguration((Configuration) it.next());
095: }
096: }
097: }
098:
099: /**
100: * Add a configuration.
101: *
102: * @param config the configuration to add
103: */
104: public void addConfiguration(Configuration config) {
105: if (!configList.contains(config)) {
106: // As the inMemoryConfiguration contains all manually added keys,
107: // we must make sure that it is always last. "Normal", non composed
108: // configuration add their keys at the end of the configuration and
109: // we want to mimic this behaviour.
110: configList.add(configList.indexOf(inMemoryConfiguration),
111: config);
112:
113: if (config instanceof AbstractConfiguration) {
114: ((AbstractConfiguration) config)
115: .setThrowExceptionOnMissing(isThrowExceptionOnMissing());
116: }
117: }
118: }
119:
120: /**
121: * Remove a configuration. The in memory configuration cannot be removed.
122: *
123: * @param config The configuration to remove
124: */
125: public void removeConfiguration(Configuration config) {
126: // Make sure that you can't remove the inMemoryConfiguration from
127: // the CompositeConfiguration object
128: if (!config.equals(inMemoryConfiguration)) {
129: configList.remove(config);
130: }
131: }
132:
133: /**
134: * Return the number of configurations.
135: *
136: * @return the number of configuration
137: */
138: public int getNumberOfConfigurations() {
139: return configList.size();
140: }
141:
142: /**
143: * Remove all configuration reinitialize the in memory configuration.
144: */
145: public void clear() {
146: configList.clear();
147: // recreate the in memory configuration
148: inMemoryConfiguration = new BaseConfiguration();
149: ((BaseConfiguration) inMemoryConfiguration)
150: .setThrowExceptionOnMissing(isThrowExceptionOnMissing());
151: ((BaseConfiguration) inMemoryConfiguration)
152: .setListDelimiter(getListDelimiter());
153: ((BaseConfiguration) inMemoryConfiguration)
154: .setDelimiterParsingDisabled(isDelimiterParsingDisabled());
155: configList.add(inMemoryConfiguration);
156: }
157:
158: /**
159: * Add this property to the inmemory Configuration.
160: *
161: * @param key The Key to add the property to.
162: * @param token The Value to add.
163: */
164: protected void addPropertyDirect(String key, Object token) {
165: inMemoryConfiguration.addProperty(key, token);
166: }
167:
168: /**
169: * Read property from underlying composite
170: *
171: * @param key key to use for mapping
172: *
173: * @return object associated with the given configuration key.
174: */
175: public Object getProperty(String key) {
176: Configuration firstMatchingConfiguration = null;
177: for (Iterator i = configList.iterator(); i.hasNext();) {
178: Configuration config = (Configuration) i.next();
179: if (config.containsKey(key)) {
180: firstMatchingConfiguration = config;
181: break;
182: }
183: }
184:
185: if (firstMatchingConfiguration != null) {
186: return firstMatchingConfiguration.getProperty(key);
187: } else {
188: return null;
189: }
190: }
191:
192: /**
193: * {@inheritDoc}
194: */
195: public Iterator getKeys() {
196: List keys = new ArrayList();
197: for (Iterator i = configList.iterator(); i.hasNext();) {
198: Configuration config = (Configuration) i.next();
199:
200: Iterator j = config.getKeys();
201: while (j.hasNext()) {
202: String key = (String) j.next();
203: if (!keys.contains(key)) {
204: keys.add(key);
205: }
206: }
207: }
208:
209: return keys.iterator();
210: }
211:
212: /**
213: * {@inheritDoc}
214: */
215: public Iterator getKeys(String key) {
216: List keys = new ArrayList();
217: for (Iterator i = configList.iterator(); i.hasNext();) {
218: Configuration config = (Configuration) i.next();
219:
220: Iterator j = config.getKeys(key);
221: while (j.hasNext()) {
222: String newKey = (String) j.next();
223: if (!keys.contains(newKey)) {
224: keys.add(newKey);
225: }
226: }
227: }
228:
229: return keys.iterator();
230: }
231:
232: /**
233: * {@inheritDoc}
234: */
235: public boolean isEmpty() {
236: boolean isEmpty = true;
237: for (Iterator i = configList.iterator(); i.hasNext();) {
238: Configuration config = (Configuration) i.next();
239: if (!config.isEmpty()) {
240: return false;
241: }
242: }
243:
244: return isEmpty;
245: }
246:
247: /**
248: * {@inheritDoc}
249: */
250: protected void clearPropertyDirect(String key) {
251: for (Iterator i = configList.iterator(); i.hasNext();) {
252: Configuration config = (Configuration) i.next();
253: config.clearProperty(key);
254: }
255: }
256:
257: /**
258: * {@inheritDoc}
259: */
260: public boolean containsKey(String key) {
261: for (Iterator i = configList.iterator(); i.hasNext();) {
262: Configuration config = (Configuration) i.next();
263: if (config.containsKey(key)) {
264: return true;
265: }
266: }
267: return false;
268: }
269:
270: /**
271: * {@inheritDoc}
272: */
273: public List getList(String key, List defaultValue) {
274: List list = new ArrayList();
275:
276: // add all elements from the first configuration containing the requested key
277: Iterator it = configList.iterator();
278: while (it.hasNext() && list.isEmpty()) {
279: Configuration config = (Configuration) it.next();
280: if (config != inMemoryConfiguration
281: && config.containsKey(key)) {
282: list.addAll(config.getList(key));
283: }
284: }
285:
286: // add all elements from the in memory configuration
287: list.addAll(inMemoryConfiguration.getList(key));
288:
289: if (list.isEmpty()) {
290: return defaultValue;
291: }
292:
293: ListIterator lit = list.listIterator();
294: while (lit.hasNext()) {
295: lit.set(interpolate(lit.next()));
296: }
297:
298: return list;
299: }
300:
301: /**
302: * {@inheritDoc}
303: */
304: public String[] getStringArray(String key) {
305: List list = getList(key);
306:
307: // interpolate the strings
308: String[] tokens = new String[list.size()];
309:
310: for (int i = 0; i < tokens.length; i++) {
311: tokens[i] = interpolate(String.valueOf(list.get(i)));
312: }
313:
314: return tokens;
315: }
316:
317: /**
318: * Return the configuration at the specified index.
319: *
320: * @param index The index of the configuration to retrieve
321: * @return the configuration at this index
322: */
323: public Configuration getConfiguration(int index) {
324: return (Configuration) configList.get(index);
325: }
326:
327: /**
328: * Returns the "in memory configuration". In this configuration
329: * changes are stored.
330: *
331: * @return the in memory configuration
332: */
333: public Configuration getInMemoryConfiguration() {
334: return inMemoryConfiguration;
335: }
336:
337: /**
338: * Returns a copy of this object. This implementation will create a deep
339: * clone, i.e. all configurations contained in this composite will also be
340: * cloned. This only works if all contained configurations support cloning;
341: * otherwise a runtime exception will be thrown. Registered event handlers
342: * won't get cloned.
343: *
344: * @return the copy
345: * @since 1.3
346: */
347: public Object clone() {
348: try {
349: CompositeConfiguration copy = (CompositeConfiguration) super
350: .clone();
351: copy.clearConfigurationListeners();
352: copy.configList = new LinkedList();
353: copy.inMemoryConfiguration = ConfigurationUtils
354: .cloneConfiguration(getInMemoryConfiguration());
355: copy.configList.add(copy.inMemoryConfiguration);
356:
357: for (int i = 0; i < getNumberOfConfigurations(); i++) {
358: Configuration config = getConfiguration(i);
359: if (config != getInMemoryConfiguration()) {
360: copy.addConfiguration(ConfigurationUtils
361: .cloneConfiguration(config));
362: }
363: }
364:
365: return copy;
366: } catch (CloneNotSupportedException cnex) {
367: // cannot happen
368: throw new ConfigurationRuntimeException(cnex);
369: }
370: }
371:
372: /**
373: * Sets a flag whether added values for string properties should be checked
374: * for the list delimiter. This implementation ensures that the in memory
375: * configuration is correctly initialized.
376: *
377: * @param delimiterParsingDisabled the new value of the flag
378: * @since 1.4
379: */
380: public void setDelimiterParsingDisabled(
381: boolean delimiterParsingDisabled) {
382: ((BaseConfiguration) getInMemoryConfiguration())
383: .setDelimiterParsingDisabled(delimiterParsingDisabled);
384: super .setDelimiterParsingDisabled(delimiterParsingDisabled);
385: }
386:
387: /**
388: * Sets the character that is used as list delimiter. This implementation
389: * ensures that the in memory configuration is correctly initialized.
390: *
391: * @param listDelimiter the new list delimiter character
392: * @since 1.4
393: */
394: public void setListDelimiter(char listDelimiter) {
395: ((BaseConfiguration) getInMemoryConfiguration())
396: .setListDelimiter(listDelimiter);
397: super.setListDelimiter(listDelimiter);
398: }
399: }
|