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

Classes in this File | Line Coverage | Branch Coverage | Complexity
AbstractConfiguration |
96% | 273/285
100% | 55/55
| 2.676470588235294;2,676
AbstractConfiguration$1 |
100% | 3/3
100% | 1/1
| 2.676470588235294;2,676
AbstractConfiguration$2 |
33% | 1/3
N/A | N/A
| 2.676470588235294;2,676
AbstractConfiguration$3 |
100% | 3/3
100% | 1/1
| 2.676470588235294;2,676

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.lang.reflect.Array;
21 |  |
     import java.math.BigDecimal;
22 |  |
     import java.math.BigInteger;
23 |  |
     import java.util.ArrayList;
24 |  |
     import java.util.Collection;
25 |  |
     import java.util.Iterator;
26 |  |
     import java.util.List;
27 |  |
     import java.util.NoSuchElementException;
28 |  |
     import java.util.Properties;
29 |  |
30 |  |
     import org.apache.commons.collections.Predicate;
31 |  |
     import org.apache.commons.collections.iterators.FilterIterator;
32 |  |
     import org.apache.commons.configuration.event.ConfigurationErrorEvent;
33 |  |
     import org.apache.commons.configuration.event.ConfigurationErrorListener;
34 |  |
     import org.apache.commons.configuration.event.EventSource;
35 |  |
     import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
36 |  |
     import org.apache.commons.lang.BooleanUtils;
37 |  |
     import org.apache.commons.lang.text.StrLookup;
38 |  |
     import org.apache.commons.lang.text.StrSubstitutor;
39 |  |
     import org.apache.commons.logging.Log;
40 |  |
     import org.apache.commons.logging.impl.NoOpLog;
41 |  |
42 |  |
     /**
43 |  |
      * <p>Abstract configuration class. Provides basic functionality but does not
44 |  |
      * store any data.</p>
45 |  |
      * <p>If you want to write your own Configuration class then you should
46 |  |
      * implement only abstract methods from this class. A lot of functionality
47 |  |
      * needed by typical implementations of the <code>Configuration</code>
48 |  |
      * interface is already provided by this base class. Following is a list of
49 |  |
      * features implemented here:
50 |  |
      * <ul><li>Data conversion support. The various data types required by the
51 |  |
      * <code>Configuration</code> interface are already handled by this base class.
52 |  |
      * A concrete sub class only needs to provide a generic <code>getProperty()</code>
53 |  |
      * method.</li>
54 |  |
      * <li>Support for variable interpolation. Property values containing special
55 |  |
      * variable tokens (like <code>${var}</code>) will be replaced by their
56 |  |
      * corresponding values.</li>
57 |  |
      * <li>Support for string lists. The values of properties to be added to this
58 |  |
      * configuration are checked whether they contain a list delimiter character. If
59 |  |
      * this is the case and if list splitting is enabled, the string is split and
60 |  |
      * multiple values are added for this property. (With the
61 |  |
      * <code>setListDelimiter()</code> method the delimiter character can be
62 |  |
      * specified; per default a comma is used. The
63 |  |
      * <code>setDelimiterParsingDisabled()</code> method can be used to disable
64 |  |
      * list splitting completely.)</li>
65 |  |
      * <li>Allows to specify how missing properties are treated. Per default the
66 |  |
      * get methods returning an object will return <b>null</b> if the searched
67 |  |
      * property key is not found (and no default value is provided). With the
68 |  |
      * <code>setThrowExceptionOnMissing()</code> method this behavior can be
69 |  |
      * changed to throw an exception when a requested property cannot be found.</li>
70 |  |
      * <li>Basic event support. Whenever this configuration is modified registered
71 |  |
      * event listeners are notified. Refer to the various <code>EVENT_XXX</code>
72 |  |
      * constants to get an impression about which event types are supported.</li>
73 |  |
      * </ul></p>
74 |  |
      *
75 |  |
      * @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov </a>
76 |  |
      * @author Oliver Heger
77 |  |
      * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen </a>
78 |  |
      * @version $Id: AbstractConfiguration.java 589138 2007-10-27 15:41:13Z oheger $
79 |  |
      */
80 |  |
     public abstract class AbstractConfiguration extends EventSource implements Configuration
81 |  |
     {
82 |  |
         /**
83 |  |
          * Constant for the add property event type.
84 |  |
          * @since 1.3
85 |  |
          */
86 |  |
         public static final int EVENT_ADD_PROPERTY = 1;
87 |  |
88 |  |
         /**
89 |  |
          * Constant for the clear property event type.
90 |  |
          * @since 1.3
91 |  |
          */
92 |  |
         public static final int EVENT_CLEAR_PROPERTY = 2;
93 |  |
94 |  |
         /**
95 |  |
          * Constant for the set property event type.
96 |  |
          * @since 1.3
97 |  |
          */
98 |  |
         public static final int EVENT_SET_PROPERTY = 3;
99 |  |
100 |  |
         /**
101 |  |
          * Constant for the clear configuration event type.
102 |  |
          * @since 1.3
103 |  |
          */
104 |  |
         public static final int EVENT_CLEAR = 4;
105 |  |
106 |  |
         /**
107 |  |
          * Constant for the get property event type. This event type is used for
108 |  |
          * error events.
109 |  |
          * @since 1.4
110 |  |
          */
111 |  |
         public static final int EVENT_READ_PROPERTY = 5;
112 |  |
113 |  |
         /** start token */
114 |  |
         protected static final String START_TOKEN = "${";
115 |  |
116 |  |
         /** end token */
117 |  |
         protected static final String END_TOKEN = "}";
118 |  |
119 |  |
         /**
120 |  |
          * Constant for the disabled list delimiter. This character is passed to the
121 |  |
          * list parsing methods if delimiter parsing is disabled. So this character
122 |  |
          * should not occur in string property values.
123 |  |
          */
124 |  |
         private static final char DISABLED_DELIMITER = '\0';
125 |  |
126 |  |
         /** The default value for listDelimiter */
127 | 62 |
         private static char defaultListDelimiter = ',';
128 |  |
129 |  |
         /** Delimiter used to convert single values to lists */
130 | 3315 |
         private char listDelimiter = defaultListDelimiter;
131 |  |
132 |  |
         /**
133 |  |
          * When set to true the given configuration delimiter will not be used
134 |  |
          * while parsing for this configuration.
135 |  |
          */
136 |  |
         private boolean delimiterParsingDisabled;
137 |  |
138 |  |
         /**
139 |  |
          * Whether the configuration should throw NoSuchElementExceptions or simply
140 |  |
          * return null when a property does not exist. Defaults to return null.
141 |  |
          */
142 |  |
         private boolean throwExceptionOnMissing;
143 |  |
144 |  |
         /** Stores a reference to the object that handles variable interpolation.*/
145 |  |
         private StrSubstitutor substitutor;
146 |  |
147 |  |
         /** Stores the logger.*/
148 |  |
         private Log log;
149 |  |
150 |  |
         /**
151 |  |
          * Creates a new instance of <code>AbstractConfiguration</code>.
152 |  |
          */
153 |  |
         public AbstractConfiguration()
154 | 3315 |
         {
155 | 3315 |
             setLogger(null);
156 | 3315 |
         }
157 |  |
158 |  |
         /**
159 |  |
          * For configurations extending AbstractConfiguration, allow them to change
160 |  |
          * the listDelimiter from the default comma (","). This value will be used
161 |  |
          * only when creating new configurations. Those already created will not be
162 |  |
          * affected by this change
163 |  |
          *
164 |  |
          * @param delimiter The new listDelimiter
165 |  |
          */
166 |  |
         public static void setDefaultListDelimiter(char delimiter)
167 |  |
         {
168 | 2 |
             AbstractConfiguration.defaultListDelimiter = delimiter;
169 | 2 |
         }
170 |  |
171 |  |
         /**
172 |  |
          * Sets the default list delimiter.
173 |  |
          *
174 |  |
          * @param delimiter the delimiter character
175 |  |
          * @deprecated Use AbstractConfiguration.setDefaultListDelimiter(char)
176 |  |
          * instead
177 |  |
          */
178 |  |
         public static void setDelimiter(char delimiter)
179 |  |
         {
180 | 0 |
             setDefaultListDelimiter(delimiter);
181 | 0 |
         }
182 |  |
183 |  |
         /**
184 |  |
          * Retrieve the current delimiter. By default this is a comma (",").
185 |  |
          *
186 |  |
          * @return The delimiter in use
187 |  |
          */
188 |  |
         public static char getDefaultListDelimiter()
189 |  |
         {
190 | 55 |
             return AbstractConfiguration.defaultListDelimiter;
191 |  |
         }
192 |  |
193 |  |
         /**
194 |  |
          * Returns the default list delimiter.
195 |  |
          *
196 |  |
          * @return the default list delimiter
197 |  |
          * @deprecated Use AbstractConfiguration.getDefaultListDelimiter() instead
198 |  |
          */
199 |  |
         public static char getDelimiter()
200 |  |
         {
201 | 0 |
             return getDefaultListDelimiter();
202 |  |
         }
203 |  |
204 |  |
         /**
205 |  |
          * Change the list delimiter for this configuration.
206 |  |
          *
207 |  |
          * Note: this change will only be effective for new parsings. If you
208 |  |
          * want it to take effect for all loaded properties use the no arg constructor
209 |  |
          * and call this method before setting the source.
210 |  |
          *
211 |  |
          * @param listDelimiter The new listDelimiter
212 |  |
          */
213 |  |
         public void setListDelimiter(char listDelimiter)
214 |  |
         {
215 | 395 |
             this.listDelimiter = listDelimiter;
216 | 395 |
         }
217 |  |
218 |  |
         /**
219 |  |
          * Retrieve the delimiter for this configuration. The default
220 |  |
          * is the value of defaultListDelimiter.
221 |  |
          *
222 |  |
          * @return The listDelimiter in use
223 |  |
          */
224 |  |
         public char getListDelimiter()
225 |  |
         {
226 | 49320 |
             return listDelimiter;
227 |  |
         }
228 |  |
229 |  |
         /**
230 |  |
          * Determine if this configuration is using delimiters when parsing
231 |  |
          * property values to convert them to lists of values. Defaults to false
232 |  |
          * @return true if delimiters are not being used
233 |  |
          */
234 |  |
         public boolean isDelimiterParsingDisabled()
235 |  |
         {
236 | 49946 |
             return delimiterParsingDisabled;
237 |  |
         }
238 |  |
239 |  |
         /**
240 |  |
          * Set whether this configuration should use delimiters when parsing
241 |  |
          * property values to convert them to lists of values. By default delimiter
242 |  |
          * parsing is enabled
243 |  |
          *
244 |  |
          * Note: this change will only be effective for new parsings. If you
245 |  |
          * want it to take effect for all loaded properties use the no arg constructor
246 |  |
          * and call this method before setting source.
247 |  |
          * @param delimiterParsingDisabled a flag whether delimiter parsing should
248 |  |
          * be disabled
249 |  |
          */
250 |  |
         public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
251 |  |
         {
252 | 490 |
             this.delimiterParsingDisabled = delimiterParsingDisabled;
253 | 490 |
         }
254 |  |
255 |  |
         /**
256 |  |
          * Allows to set the <code>throwExceptionOnMissing</code> flag. This
257 |  |
          * flag controls the behavior of property getter methods that return
258 |  |
          * objects if the requested property is missing. If the flag is set to
259 |  |
          * <b>false</b> (which is the default value), these methods will return
260 |  |
          * <b>null</b>. If set to <b>true</b>, they will throw a
261 |  |
          * <code>NoSuchElementException</code> exception. Note that getter methods
262 |  |
          * for primitive data types are not affected by this flag.
263 |  |
          *
264 |  |
          * @param throwExceptionOnMissing The new value for the property
265 |  |
          */
266 |  |
         public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
267 |  |
         {
268 | 802 |
             this.throwExceptionOnMissing = throwExceptionOnMissing;
269 | 802 |
         }
270 |  |
271 |  |
         /**
272 |  |
          * Returns true if missing values throw Exceptions.
273 |  |
          *
274 |  |
          * @return true if missing values throw Exceptions
275 |  |
          */
276 |  |
         public boolean isThrowExceptionOnMissing()
277 |  |
         {
278 | 782 |
             return throwExceptionOnMissing;
279 |  |
         }
280 |  |
281 |  |
         /**
282 |  |
          * Returns the object that is responsible for variable interpolation.
283 |  |
          *
284 |  |
          * @return the object responsible for variable interpolation
285 |  |
          * @since 1.4
286 |  |
          */
287 |  |
         public synchronized StrSubstitutor getSubstitutor()
288 |  |
         {
289 | 2079 |
             if (substitutor == null)
290 |  |
             {
291 | 509 |
                 substitutor = new StrSubstitutor(createInterpolator());
292 |  |
             }
293 | 2079 |
             return substitutor;
294 |  |
         }
295 |  |
296 |  |
         /**
297 |  |
          * Returns the <code>ConfigurationInterpolator</code> object that manages
298 |  |
          * the lookup objects for resolving variables. <em>Note:</em> If this
299 |  |
          * object is manipulated (e.g. new lookup objects added), synchronisation
300 |  |
          * has to be manually ensured. Because
301 |  |
          * <code>ConfigurationInterpolator</code> is not thread-safe concurrent
302 |  |
          * access to properties of this configuration instance (which causes the
303 |  |
          * interpolator to be invoked) may cause race conditions.
304 |  |
          *
305 |  |
          * @return the <code>ConfigurationInterpolator</code> associated with this
306 |  |
          * configuration
307 |  |
          * @since 1.4
308 |  |
          */
309 |  |
         public ConfigurationInterpolator getInterpolator()
310 |  |
         {
311 | 2 |
             return (ConfigurationInterpolator) getSubstitutor()
312 |  |
                     .getVariableResolver();
313 |  |
         }
314 |  |
315 |  |
         /**
316 |  |
          * Creates the interpolator object that is responsible for variable
317 |  |
          * interpolation. This method is invoked on first access of the
318 |  |
          * interpolation features. It creates a new instance of
319 |  |
          * <code>ConfigurationInterpolator</code> and sets the default lookup
320 |  |
          * object to an implementation that queries this configuration.
321 |  |
          *
322 |  |
          * @return the newly created interpolator object
323 |  |
          * @since 1.4
324 |  |
          */
325 |  |
         protected ConfigurationInterpolator createInterpolator()
326 |  |
         {
327 | 509 |
             ConfigurationInterpolator interpol = new ConfigurationInterpolator();
328 | 509 |
             interpol.setDefaultLookup(new StrLookup()
329 |  |
             {
330 | 509 |
                 public String lookup(String var)
331 |  |
                 {
332 | 165 |
                     Object prop = resolveContainerStore(var);
333 | 165 |
                     return (prop != null) ? prop.toString() : null;
334 |  |
                 }
335 |  |
             });
336 | 509 |
             return interpol;
337 |  |
         }
338 |  |
339 |  |
         /**
340 |  |
          * Returns the logger used by this configuration object.
341 |  |
          *
342 |  |
          * @return the logger
343 |  |
          * @since 1.4
344 |  |
          */
345 |  |
         public Log getLogger()
346 |  |
         {
347 | 229 |
             return log;
348 |  |
         }
349 |  |
350 |  |
         /**
351 |  |
          * Allows to set the logger to be used by this configuration object. This
352 |  |
          * method makes it possible for clients to exactly control logging behavior.
353 |  |
          * Per default a logger is set that will ignore all log messages. Derived
354 |  |
          * classes that want to enable logging should call this method during their
355 |  |
          * initialization with the logger to be used.
356 |  |
          *
357 |  |
          * @param log the new logger
358 |  |
          * @since 1.4
359 |  |
          */
360 |  |
         public void setLogger(Log log)
361 |  |
         {
362 | 4676 |
             this.log = (log != null) ? log : new NoOpLog();
363 | 4676 |
         }
364 |  |
365 |  |
         /**
366 |  |
          * Adds a special
367 |  |
          * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>
368 |  |
          * object to this configuration that will log all internal errors. This
369 |  |
          * method is intended to be used by certain derived classes, for which it is
370 |  |
          * known that they can fail on property access (e.g.
371 |  |
          * <code>DatabaseConfiguration</code>).
372 |  |
          *
373 |  |
          * @since 1.4
374 |  |
          */
375 |  |
         public void addErrorLogListener()
376 |  |
         {
377 | 1355 |
             addErrorListener(new ConfigurationErrorListener()
378 |  |
             {
379 | 1355 |
                 public void configurationError(ConfigurationErrorEvent event)
380 |  |
                 {
381 | 0 |
                     getLogger().warn("Internal error", event.getCause());
382 | 0 |
                 }
383 |  |
             });
384 | 1355 |
         }
385 |  |
386 |  |
         public void addProperty(String key, Object value)
387 |  |
         {
388 | 34233 |
             fireEvent(EVENT_ADD_PROPERTY, key, value, true);
389 | 34233 |
             addPropertyValues(key, value,
390 |  |
                     isDelimiterParsingDisabled() ? DISABLED_DELIMITER
391 |  |
                             : getListDelimiter());
392 | 34230 |
             fireEvent(EVENT_ADD_PROPERTY, key, value, false);
393 | 34230 |
         }
394 |  |
395 |  |
         /**
396 |  |
          * Adds a key/value pair to the Configuration. Override this method to
397 |  |
          * provide write access to underlying Configuration store.
398 |  |
          *
399 |  |
          * @param key key to use for mapping
400 |  |
          * @param value object to store
401 |  |
          */
402 |  |
         protected abstract void addPropertyDirect(String key, Object value);
403 |  |
404 |  |
         /**
405 |  |
          * Adds the specified value for the given property. This method supports
406 |  |
          * single values and containers (e.g. collections or arrays) as well. In the
407 |  |
          * latter case, <code>addPropertyDirect()</code> will be called for each
408 |  |
          * element.
409 |  |
          *
410 |  |
          * @param key the property key
411 |  |
          * @param value the value object
412 |  |
          * @param delimiter the list delimiter character
413 |  |
          */
414 |  |
         private void addPropertyValues(String key, Object value, char delimiter)
415 |  |
         {
416 | 34331 |
             Iterator it = PropertyConverter.toIterator(value, delimiter);
417 | 72116 |
             while (it.hasNext())
418 |  |
             {
419 | 37788 |
                 addPropertyDirect(key, it.next());
420 | 37785 |
             }
421 | 34328 |
         }
422 |  |
423 |  |
         /**
424 |  |
          * interpolate key names to handle ${key} stuff
425 |  |
          *
426 |  |
          * @param base string to interpolate
427 |  |
          *
428 |  |
          * @return returns the key name with the ${key} substituted
429 |  |
          */
430 |  |
         protected String interpolate(String base)
431 |  |
         {
432 | 990 |
             Object result = interpolate((Object) base);
433 | 987 |
             return (result == null) ? null : result.toString();
434 |  |
         }
435 |  |
436 |  |
         /**
437 |  |
          * Returns the interpolated value. Non String values are returned without change.
438 |  |
          *
439 |  |
          * @param value the value to interpolate
440 |  |
          *
441 |  |
          * @return returns the value with variables substituted
442 |  |
          */
443 |  |
         protected Object interpolate(Object value)
444 |  |
         {
445 | 2820 |
             return PropertyConverter.interpolate(value, this);
446 |  |
         }
447 |  |
448 |  |
         /**
449 |  |
          * Recursive handler for multple levels of interpolation.
450 |  |
          *
451 |  |
          * When called the first time, priorVariables should be null.
452 |  |
          *
453 |  |
          * @param base string with the ${key} variables
454 |  |
          * @param priorVariables serves two purposes: to allow checking for loops,
455 |  |
          * and creating a meaningful exception message should a loop occur. It's
456 |  |
          * 0'th element will be set to the value of base from the first call. All
457 |  |
          * subsequent interpolated variables are added afterward.
458 |  |
          *
459 |  |
          * @return the string with the interpolation taken care of
460 |  |
          * @deprecated Interpolation is now handled by
461 |  |
          * <code>{@link PropertyConverter}</code>; this method will no longer be
462 |  |
          * called
463 |  |
          */
464 |  |
         protected String interpolateHelper(String base, List priorVariables)
465 |  |
         {
466 | 0 |
             return base; // just a dummy implementation
467 |  |
         }
468 |  |
469 |  |
         public Configuration subset(String prefix)
470 |  |
         {
471 | 43 |
             return new SubsetConfiguration(this, prefix, ".");
472 |  |
         }
473 |  |
474 |  |
         public void setProperty(String key, Object value)
475 |  |
         {
476 | 822 |
             fireEvent(EVENT_SET_PROPERTY, key, value, true);
477 | 822 |
             setDetailEvents(false);
478 |  |
             try
479 |  |
             {
480 | 822 |
                 clearProperty(key);
481 | 821 |
                 addProperty(key, value);
482 |  |
             }
483 |  |
             finally
484 |  |
             {
485 | 822 |
                 setDetailEvents(true);
486 | 822 |
             }
487 | 821 |
             fireEvent(EVENT_SET_PROPERTY, key, value, false);
488 | 821 |
         }
489 |  |
490 |  |
         /**
491 |  |
          * Removes the specified property from this configuration. This
492 |  |
          * implementation performs some preparations and then delegates to
493 |  |
          * <code>clearPropertyDirect()</code>, which will do the real work.
494 |  |
          *
495 |  |
          * @param key the key to be removed
496 |  |
          */
497 |  |
         public void clearProperty(String key)
498 |  |
         {
499 | 943 |
             fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
500 | 943 |
             clearPropertyDirect(key);
501 | 943 |
             fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
502 | 943 |
         }
503 |  |
504 |  |
         /**
505 |  |
          * Removes the specified property from this configuration. This method is
506 |  |
          * called by <code>clearProperty()</code> after it has done some
507 |  |
          * preparations. It should be overriden in sub classes. This base
508 |  |
          * implementation is just left empty.
509 |  |
          *
510 |  |
          * @param key the key to be removed
511 |  |
          */
512 |  |
         protected void clearPropertyDirect(String key)
513 |  |
         {
514 |  |
             // override in sub classes
515 | 0 |
         }
516 |  |
517 |  |
         public void clear()
518 |  |
         {
519 | 49 |
             fireEvent(EVENT_CLEAR, null, null, true);
520 | 49 |
             setDetailEvents(false);
521 | 49 |
             boolean useIterator = true;
522 |  |
             try
523 |  |
             {
524 | 49 |
                 Iterator it = getKeys();
525 | 685 |
                 while (it.hasNext())
526 |  |
                 {
527 | 636 |
                     String key = (String) it.next();
528 | 636 |
                     if (useIterator)
529 |  |
                     {
530 |  |
                         try
531 |  |
                         {
532 | 49 |
                             it.remove();
533 |  |
                         }
534 | 1 |
                         catch (UnsupportedOperationException usoex)
535 |  |
                         {
536 | 1 |
                             useIterator = false;
537 | 48 |
                         }
538 |  |
                     }
539 |  |
540 | 636 |
                     if (useIterator && containsKey(key))
541 |  |
                     {
542 | 45 |
                         useIterator = false;
543 |  |
                     }
544 |  |
545 | 636 |
                     if (!useIterator)
546 |  |
                     {
547 |  |
                         // workaround for Iterators that do not remove the property
548 |  |
                         // on calling remove() or do not support remove() at all
549 | 633 |
                         clearProperty(key);
550 |  |
                     }
551 | 636 |
                 }
552 |  |
             }
553 |  |
             finally
554 |  |
             {
555 | 49 |
                 setDetailEvents(true);
556 | 49 |
             }
557 | 49 |
             fireEvent(EVENT_CLEAR, null, null, false);
558 | 49 |
         }
559 |  |
560 |  |
         public Iterator getKeys(final String prefix)
561 |  |
         {
562 | 41 |
             return new FilterIterator(getKeys(), new Predicate()
563 |  |
             {
564 | 41 |
                 public boolean evaluate(Object obj)
565 |  |
                 {
566 | 450 |
                     String key = (String) obj;
567 | 450 |
                     return key.startsWith(prefix + ".") || key.equals(prefix);
568 |  |
                 }
569 |  |
             });
570 |  |
         }
571 |  |
572 |  |
         public Properties getProperties(String key)
573 |  |
         {
574 | 4 |
             return getProperties(key, null);
575 |  |
         }
576 |  |
577 |  |
         /**
578 |  |
          * Get a list of properties associated with the given configuration key.
579 |  |
          *
580 |  |
          * @param key The configuration key.
581 |  |
          * @param defaults Any default values for the returned
582 |  |
          * <code>Properties</code> object. Ignored if <code>null</code>.
583 |  |
          *
584 |  |
          * @return The associated properties if key is found.
585 |  |
          *
586 |  |
          * @throws ConversionException is thrown if the key maps to an object that
587 |  |
          * is not a String/List of Strings.
588 |  |
          *
589 |  |
          * @throws IllegalArgumentException if one of the tokens is malformed (does
590 |  |
          * not contain an equals sign).
591 |  |
          */
592 |  |
         public Properties getProperties(String key, Properties defaults)
593 |  |
         {
594 |  |
             /*
595 |  |
              * Grab an array of the tokens for this key.
596 |  |
              */
597 | 4 |
             String[] tokens = getStringArray(key);
598 |  |
599 |  |
             /*
600 |  |
              * Each token is of the form 'key=value'.
601 |  |
              */
602 | 4 |
             Properties props = defaults == null ? new Properties() : new Properties(defaults);
603 | 10 |
             for (int i = 0; i < tokens.length; i++)
604 |  |
             {
605 | 8 |
                 String token = tokens[i];
606 | 8 |
                 int equalSign = token.indexOf('=');
607 | 8 |
                 if (equalSign > 0)
608 |  |
                 {
609 | 6 |
                     String pkey = token.substring(0, equalSign).trim();
610 | 6 |
                     String pvalue = token.substring(equalSign + 1).trim();
611 | 6 |
                     props.put(pkey, pvalue);
612 | 6 |
                 }
613 | 2 |
                 else if (tokens.length == 1 && "".equals(token))
614 |  |
                 {
615 |  |
                     // Semantically equivalent to an empty Properties
616 |  |
                     // object.
617 | 2 |
                     break;
618 |  |
                 }
619 |  |
                 else
620 |  |
                 {
621 | 0 |
                     throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign");
622 |  |
                 }
623 |  |
             }
624 | 4 |
             return props;
625 |  |
         }
626 |  |
627 |  |
         /**
628 |  |
          * {@inheritDoc}
629 |  |
          * @see PropertyConverter#toBoolean(Object)
630 |  |
          */
631 |  |
         public boolean getBoolean(String key)
632 |  |
         {
633 | 64 |
             Boolean b = getBoolean(key, null);
634 | 62 |
             if (b != null)
635 |  |
             {
636 | 60 |
                 return b.booleanValue();
637 |  |
             }
638 |  |
             else
639 |  |
             {
640 | 2 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
641 |  |
             }
642 |  |
         }
643 |  |
644 |  |
         /**
645 |  |
          * {@inheritDoc}
646 |  |
          * @see PropertyConverter#toBoolean(Object)
647 |  |
          */
648 |  |
         public boolean getBoolean(String key, boolean defaultValue)
649 |  |
         {
650 | 24 |
             return getBoolean(key, BooleanUtils.toBooleanObject(defaultValue)).booleanValue();
651 |  |
         }
652 |  |
653 |  |
         /**
654 |  |
          * Obtains the value of the specified key and tries to convert it into a
655 |  |
          * <code>Boolean</code> object. If the property has no value, the passed
656 |  |
          * in default value will be used.
657 |  |
          *
658 |  |
          * @param key the key of the property
659 |  |
          * @param defaultValue the default value
660 |  |
          * @return the value of this key converted to a <code>Boolean</code>
661 |  |
          * @throws ConversionException if the value cannot be converted to a
662 |  |
          * <code>Boolean</code>
663 |  |
          * @see PropertyConverter#toBoolean(Object)
664 |  |
          */
665 |  |
         public Boolean getBoolean(String key, Boolean defaultValue)
666 |  |
         {
667 | 124 |
             Object value = resolveContainerStore(key);
668 |  |
669 | 124 |
             if (value == null)
670 |  |
             {
671 | 37 |
                 return defaultValue;
672 |  |
             }
673 |  |
             else
674 |  |
             {
675 |  |
                 try
676 |  |
                 {
677 | 87 |
                     return PropertyConverter.toBoolean(interpolate(value));
678 |  |
                 }
679 | 3 |
                 catch (ConversionException e)
680 |  |
                 {
681 | 3 |
                     throw new ConversionException('\'' + key + "' doesn't map to a Boolean object", e);
682 |  |
                 }
683 |  |
             }
684 |  |
         }
685 |  |
686 |  |
         public byte getByte(String key)
687 |  |
         {
688 | 15 |
             Byte b = getByte(key, null);
689 | 13 |
             if (b != null)
690 |  |
             {
691 | 11 |
                 return b.byteValue();
692 |  |
             }
693 |  |
             else
694 |  |
             {
695 | 2 |
                 throw new NoSuchElementException('\'' + key + " doesn't map to an existing object");
696 |  |
             }
697 |  |
         }
698 |  |
699 |  |
         public byte getByte(String key, byte defaultValue)
700 |  |
         {
701 | 4 |
             return getByte(key, new Byte(defaultValue)).byteValue();
702 |  |
         }
703 |  |
704 |  |
         public Byte getByte(String key, Byte defaultValue)
705 |  |
         {
706 | 22 |
             Object value = resolveContainerStore(key);
707 |  |
708 | 22 |
             if (value == null)
709 |  |
             {
710 | 4 |
                 return defaultValue;
711 |  |
             }
712 |  |
             else
713 |  |
             {
714 |  |
                 try
715 |  |
                 {
716 | 18 |
                     return PropertyConverter.toByte(interpolate(value));
717 |  |
                 }
718 | 2 |
                 catch (ConversionException e)
719 |  |
                 {
720 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a Byte object", e);
721 |  |
                 }
722 |  |
             }
723 |  |
         }
724 |  |
725 |  |
         public double getDouble(String key)
726 |  |
         {
727 | 17 |
             Double d = getDouble(key, null);
728 | 15 |
             if (d != null)
729 |  |
             {
730 | 13 |
                 return d.doubleValue();
731 |  |
             }
732 |  |
             else
733 |  |
             {
734 | 2 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
735 |  |
             }
736 |  |
         }
737 |  |
738 |  |
         public double getDouble(String key, double defaultValue)
739 |  |
         {
740 | 11 |
             return getDouble(key, new Double(defaultValue)).doubleValue();
741 |  |
         }
742 |  |
743 |  |
         public Double getDouble(String key, Double defaultValue)
744 |  |
         {
745 | 31 |
             Object value = resolveContainerStore(key);
746 |  |
747 | 31 |
             if (value == null)
748 |  |
             {
749 | 11 |
                 return defaultValue;
750 |  |
             }
751 |  |
             else
752 |  |
             {
753 |  |
                 try
754 |  |
                 {
755 | 20 |
                     return PropertyConverter.toDouble(interpolate(value));
756 |  |
                 }
757 | 2 |
                 catch (ConversionException e)
758 |  |
                 {
759 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a Double object", e);
760 |  |
                 }
761 |  |
             }
762 |  |
         }
763 |  |
764 |  |
         public float getFloat(String key)
765 |  |
         {
766 | 14 |
             Float f = getFloat(key, null);
767 | 12 |
             if (f != null)
768 |  |
             {
769 | 10 |
                 return f.floatValue();
770 |  |
             }
771 |  |
             else
772 |  |
             {
773 | 2 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
774 |  |
             }
775 |  |
         }
776 |  |
777 |  |
         public float getFloat(String key, float defaultValue)
778 |  |
         {
779 | 7 |
             return getFloat(key, new Float(defaultValue)).floatValue();
780 |  |
         }
781 |  |
782 |  |
         public Float getFloat(String key, Float defaultValue)
783 |  |
         {
784 | 24 |
             Object value = resolveContainerStore(key);
785 |  |
786 | 24 |
             if (value == null)
787 |  |
             {
788 | 7 |
                 return defaultValue;
789 |  |
             }
790 |  |
             else
791 |  |
             {
792 |  |
                 try
793 |  |
                 {
794 | 17 |
                     return PropertyConverter.toFloat(interpolate(value));
795 |  |
                 }
796 | 2 |
                 catch (ConversionException e)
797 |  |
                 {
798 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a Float object", e);
799 |  |
                 }
800 |  |
             }
801 |  |
         }
802 |  |
803 |  |
         public int getInt(String key)
804 |  |
         {
805 | 57 |
             Integer i = getInteger(key, null);
806 | 57 |
             if (i != null)
807 |  |
             {
808 | 57 |
                 return i.intValue();
809 |  |
             }
810 |  |
             else
811 |  |
             {
812 | 0 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
813 |  |
             }
814 |  |
         }
815 |  |
816 |  |
         public int getInt(String key, int defaultValue)
817 |  |
         {
818 | 3 |
             Integer i = getInteger(key, null);
819 |  |
820 | 3 |
             if (i == null)
821 |  |
             {
822 | 3 |
                 return defaultValue;
823 |  |
             }
824 |  |
825 | 0 |
             return i.intValue();
826 |  |
         }
827 |  |
828 |  |
         public Integer getInteger(String key, Integer defaultValue)
829 |  |
         {
830 | 61 |
             Object value = resolveContainerStore(key);
831 |  |
832 | 61 |
             if (value == null)
833 |  |
             {
834 | 3 |
                 return defaultValue;
835 |  |
             }
836 |  |
             else
837 |  |
             {
838 |  |
                 try
839 |  |
                 {
840 | 58 |
                     return PropertyConverter.toInteger(interpolate(value));
841 |  |
                 }
842 | 0 |
                 catch (ConversionException e)
843 |  |
                 {
844 | 0 |
                     throw new ConversionException('\'' + key + "' doesn't map to an Integer object", e);
845 |  |
                 }
846 |  |
             }
847 |  |
         }
848 |  |
849 |  |
         public long getLong(String key)
850 |  |
         {
851 | 15 |
             Long l = getLong(key, null);
852 | 13 |
             if (l != null)
853 |  |
             {
854 | 11 |
                 return l.longValue();
855 |  |
             }
856 |  |
             else
857 |  |
             {
858 | 2 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
859 |  |
             }
860 |  |
         }
861 |  |
862 |  |
         public long getLong(String key, long defaultValue)
863 |  |
         {
864 | 7 |
             return getLong(key, new Long(defaultValue)).longValue();
865 |  |
         }
866 |  |
867 |  |
         public Long getLong(String key, Long defaultValue)
868 |  |
         {
869 | 26 |
             Object value = resolveContainerStore(key);
870 |  |
871 | 26 |
             if (value == null)
872 |  |
             {
873 | 7 |
                 return defaultValue;
874 |  |
             }
875 |  |
             else
876 |  |
             {
877 |  |
                 try
878 |  |
                 {
879 | 19 |
                     return PropertyConverter.toLong(interpolate(value));
880 |  |
                 }
881 | 2 |
                 catch (ConversionException e)
882 |  |
                 {
883 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a Long object", e);
884 |  |
                 }
885 |  |
             }
886 |  |
         }
887 |  |
888 |  |
         public short getShort(String key)
889 |  |
         {
890 | 17 |
             Short s = getShort(key, null);
891 | 15 |
             if (s != null)
892 |  |
             {
893 | 13 |
                 return s.shortValue();
894 |  |
             }
895 |  |
             else
896 |  |
             {
897 | 2 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
898 |  |
             }
899 |  |
         }
900 |  |
901 |  |
         public short getShort(String key, short defaultValue)
902 |  |
         {
903 | 7 |
             return getShort(key, new Short(defaultValue)).shortValue();
904 |  |
         }
905 |  |
906 |  |
         public Short getShort(String key, Short defaultValue)
907 |  |
         {
908 | 31 |
             Object value = resolveContainerStore(key);
909 |  |
910 | 31 |
             if (value == null)
911 |  |
             {
912 | 9 |
                 return defaultValue;
913 |  |
             }
914 |  |
             else
915 |  |
             {
916 |  |
                 try
917 |  |
                 {
918 | 22 |
                     return PropertyConverter.toShort(interpolate(value));
919 |  |
                 }
920 | 2 |
                 catch (ConversionException e)
921 |  |
                 {
922 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a Short object", e);
923 |  |
                 }
924 |  |
             }
925 |  |
         }
926 |  |
927 |  |
         /**
928 |  |
          * {@inheritDoc}
929 |  |
          * @see #setThrowExceptionOnMissing(boolean)
930 |  |
          */
931 |  |
         public BigDecimal getBigDecimal(String key)
932 |  |
         {
933 | 7 |
             BigDecimal number = getBigDecimal(key, null);
934 | 5 |
             if (number != null)
935 |  |
             {
936 | 3 |
                 return number;
937 |  |
             }
938 | 2 |
             else if (isThrowExceptionOnMissing())
939 |  |
             {
940 | 1 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
941 |  |
             }
942 |  |
             else
943 |  |
             {
944 | 1 |
                 return null;
945 |  |
             }
946 |  |
         }
947 |  |
948 |  |
         public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
949 |  |
         {
950 | 12 |
             Object value = resolveContainerStore(key);
951 |  |
952 | 12 |
             if (value == null)
953 |  |
             {
954 | 4 |
                 return defaultValue;
955 |  |
             }
956 |  |
             else
957 |  |
             {
958 |  |
                 try
959 |  |
                 {
960 | 8 |
                     return PropertyConverter.toBigDecimal(interpolate(value));
961 |  |
                 }
962 | 2 |
                 catch (ConversionException e)
963 |  |
                 {
964 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
965 |  |
                 }
966 |  |
             }
967 |  |
         }
968 |  |
969 |  |
         /**
970 |  |
          * {@inheritDoc}
971 |  |
          * @see #setThrowExceptionOnMissing(boolean)
972 |  |
          */
973 |  |
         public BigInteger getBigInteger(String key)
974 |  |
         {
975 | 8 |
             BigInteger number = getBigInteger(key, null);
976 | 6 |
             if (number != null)
977 |  |
             {
978 | 4 |
                 return number;
979 |  |
             }
980 | 2 |
             else if (isThrowExceptionOnMissing())
981 |  |
             {
982 | 1 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
983 |  |
             }
984 |  |
             else
985 |  |
             {
986 | 1 |
                 return null;
987 |  |
             }
988 |  |
         }
989 |  |
990 |  |
         public BigInteger getBigInteger(String key, BigInteger defaultValue)
991 |  |
         {
992 | 13 |
             Object value = resolveContainerStore(key);
993 |  |
994 | 13 |
             if (value == null)
995 |  |
             {
996 | 4 |
                 return defaultValue;
997 |  |
             }
998 |  |
             else
999 |  |
             {
1000 |  |
                 try
1001 |  |
                 {
1002 | 9 |
                     return PropertyConverter.toBigInteger(interpolate(value));
1003 |  |
                 }
1004 | 2 |
                 catch (ConversionException e)
1005 |  |
                 {
1006 | 2 |
                     throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
1007 |  |
                 }
1008 |  |
             }
1009 |  |
         }
1010 |  |
1011 |  |
         /**
1012 |  |
          * {@inheritDoc}
1013 |  |
          * @see #setThrowExceptionOnMissing(boolean)
1014 |  |
          */
1015 |  |
         public String getString(String key)
1016 |  |
         {
1017 | 800 |
             String s = getString(key, null);
1018 | 797 |
             if (s != null)
1019 |  |
             {
1020 | 566 |
                 return s;
1021 |  |
             }
1022 | 231 |
             else if (isThrowExceptionOnMissing())
1023 |  |
             {
1024 | 7 |
                 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
1025 |  |
             }
1026 |  |
             else
1027 |  |
             {
1028 | 224 |
                 return null;
1029 |  |
             }
1030 |  |
         }
1031 |  |
1032 |  |
         public String getString(String key, String defaultValue)
1033 |  |
         {
1034 | 880 |
             Object value = resolveContainerStore(key);
1035 |  |
1036 | 880 |
             if (value instanceof String)
1037 |  |
             {
1038 | 641 |
                 return interpolate((String) value);
1039 |  |
             }
1040 | 239 |
             else if (value == null)
1041 |  |
             {
1042 | 239 |
                 return interpolate(defaultValue);
1043 |  |
             }
1044 |  |
             else
1045 |  |
             {
1046 | 0 |
                 throw new ConversionException('\'' + key + "' doesn't map to a String object");
1047 |  |
             }
1048 |  |
         }
1049 |  |
1050 |  |
         /**
1051 |  |
          * Get an array of strings associated with the given configuration key.
1052 |  |
          * If the key doesn't map to an existing object, an empty array is returned.
1053 |  |
          * If a property is added to a configuration, it is checked whether it
1054 |  |
          * contains multiple values. This is obvious if the added object is a list
1055 |  |
          * or an array. For strings it is checked whether the string contains the
1056 |  |
          * list delimiter character that can be specified using the
1057 |  |
          * <code>setListDelimiter()</code> method. If this is the case, the string
1058 |  |
          * is splitted at these positions resulting in a property with multiple
1059 |  |
          * values.
1060 |  |
          *
1061 |  |
          * @param key The configuration key.
1062 |  |
          * @return The associated string array if key is found.
1063 |  |
          *
1064 |  |
          * @throws ConversionException is thrown if the key maps to an
1065 |  |
          *         object that is not a String/List of Strings.
1066 |  |
          * @see #setListDelimiter(char)
1067 |  |
          * @see #setDelimiterParsingDisabled(boolean)
1068 |  |
          */
1069 |  |
         public String[] getStringArray(String key)
1070 |  |
         {
1071 | 22 |
             Object value = getProperty(key);
1072 |  |
1073 |  |
             String[] array;
1074 |  |
1075 | 22 |
             if (value instanceof String)
1076 |  |
             {
1077 | 12 |
                 array = new String[1];
1078 |  |
1079 | 12 |
                 array[0] = interpolate((String) value);
1080 | 12 |
             }
1081 | 10 |
             else if (value instanceof List)
1082 |  |
             {
1083 | 8 |
                 List list = (List) value;
1084 | 8 |
                 array = new String[list.size()];
1085 |  |
1086 | 30 |
                 for (int i = 0; i < array.length; i++)
1087 |  |
                 {
1088 | 22 |
                     array[i] = interpolate((String) list.get(i));
1089 |  |
                 }
1090 | 8 |
             }
1091 | 2 |
             else if (value == null)
1092 |  |
             {
1093 | 2 |
                 array = new String[0];
1094 | 2 |
             }
1095 |  |
             else
1096 |  |
             {
1097 | 0 |
                 throw new ConversionException('\'' + key + "' doesn't map to a String/List object");
1098 |  |
             }
1099 | 22 |
             return array;
1100 |  |
         }
1101 |  |
1102 |  |
         /**
1103 |  |
          * {@inheritDoc}
1104 |  |
          * @see #getStringArray(String)
1105 |  |
          */
1106 |  |
         public List getList(String key)
1107 |  |
         {
1108 | 323 |
             return getList(key, new ArrayList());
1109 |  |
         }
1110 |  |
1111 |  |
         public List getList(String key, List defaultValue)
1112 |  |
         {
1113 | 280 |
             Object value = getProperty(key);
1114 |  |
             List list;
1115 |  |
1116 | 280 |
             if (value instanceof String)
1117 |  |
             {
1118 | 56 |
                 list = new ArrayList(1);
1119 | 56 |
                 list.add(interpolate((String) value));
1120 | 56 |
             }
1121 | 224 |
             else if (value instanceof List)
1122 |  |
             {
1123 | 165 |
                 list = new ArrayList();
1124 | 165 |
                 List l = (List) value;
1125 |  |
1126 |  |
                 // add the interpolated elements in the new list
1127 | 165 |
                 Iterator it = l.iterator();
1128 | 802 |
                 while (it.hasNext())
1129 |  |
                 {
1130 | 637 |
                     list.add(interpolate(it.next()));
1131 | 637 |
                 }
1132 | 165 |
             }
1133 | 59 |
             else if (value == null)
1134 |  |
             {
1135 | 57 |
                 list = defaultValue;
1136 | 57 |
             }
1137 |  |
             else
1138 |  |
             {
1139 | 2 |
                 throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a "
1140 |  |
                         + value.getClass().getName());
1141 |  |
             }
1142 | 278 |
             return list;
1143 |  |
         }
1144 |  |
1145 |  |
         /**
1146 |  |
          * Returns an object from the store described by the key. If the value is a
1147 |  |
          * Collection object, replace it with the first object in the collection.
1148 |  |
          *
1149 |  |
          * @param key The property key.
1150 |  |
          *
1151 |  |
          * @return value Value, transparently resolving a possible collection dependency.
1152 |  |
          */
1153 |  |
         protected Object resolveContainerStore(String key)
1154 |  |
         {
1155 | 1443 |
             Object value = getProperty(key);
1156 | 1443 |
             if (value != null)
1157 |  |
             {
1158 | 1092 |
                 if (value instanceof Collection)
1159 |  |
                 {
1160 | 13 |
                     Collection collection = (Collection) value;
1161 | 13 |
                     value = collection.isEmpty() ? null : collection.iterator().next();
1162 | 13 |
                 }
1163 | 1079 |
                 else if (value.getClass().isArray() && Array.getLength(value) > 0)
1164 |  |
                 {
1165 | 8 |
                     value = Array.get(value, 0);
1166 |  |
                 }
1167 |  |
             }
1168 |  |
1169 | 1443 |
             return value;
1170 |  |
         }
1171 |  |
1172 |  |
         /**
1173 |  |
          * Copies the content of the specified configuration into this
1174 |  |
          * configuration. If the specified configuration contains a key that is also
1175 |  |
          * present in this configuration, the value of this key will be replaced by
1176 |  |
          * the new value. <em>Note:</em> This method won't work well when copying
1177 |  |
          * hierarchical configurations because it is not able to copy information
1178 |  |
          * about the properties' structure (i.e. the parent-child-relationships will
1179 |  |
          * get lost). So when dealing with hierarchical configuration objects their
1180 |  |
          * <code>{@link HierarchicalConfiguration#clone() clone()}</code> methods
1181 |  |
          * should be used.
1182 |  |
          *
1183 |  |
          * @param c the configuration to copy (can be <b>null</b>, then this
1184 |  |
          * operation will have no effect)
1185 |  |
          * @since 1.5
1186 |  |
          */
1187 |  |
         public void copy(Configuration c)
1188 |  |
         {
1189 | 5 |
             if (c != null)
1190 |  |
             {
1191 | 4 |
                 for (Iterator it = c.getKeys(); it.hasNext();)
1192 |  |
                 {
1193 | 49 |
                     String key = (String) it.next();
1194 | 49 |
                     Object value = c.getProperty(key);
1195 | 49 |
                     fireEvent(EVENT_SET_PROPERTY, key, value, true);
1196 | 49 |
                     setDetailEvents(false);
1197 |  |
                     try
1198 |  |
                     {
1199 | 49 |
                         clearProperty(key);
1200 | 49 |
                         addPropertyValues(key, value, DISABLED_DELIMITER);
1201 |  |
                     }
1202 |  |
                     finally
1203 |  |
                     {
1204 | 49 |
                         setDetailEvents(true);
1205 | 49 |
                     }
1206 | 49 |
                     fireEvent(EVENT_SET_PROPERTY, key, value, false);
1207 | 49 |
                 }
1208 |  |
             }
1209 | 5 |
         }
1210 |  |
1211 |  |
         /**
1212 |  |
          * Appends the content of the specified configuration to this configuration.
1213 |  |
          * The values of all properties contained in the specified configuration
1214 |  |
          * will be appended to this configuration. So if a property is already
1215 |  |
          * present in this configuration, its new value will be a union of the
1216 |  |
          * values in both configurations. <em>Note:</em> This method won't work
1217 |  |
          * well when appending hierarchical configurations because it is not able to
1218 |  |
          * copy information about the properties' structure (i.e. the
1219 |  |
          * parent-child-relationships will get lost). So when dealing with
1220 |  |
          * hierarchical configuration objects their
1221 |  |
          * <code>{@link HierarchicalConfiguration#clone() clone()}</code> methods
1222 |  |
          * should be used.
1223 |  |
          *
1224 |  |
          * @param c the configuration to be appended (can be <b>null</b>, then this
1225 |  |
          * operation will have no effect)
1226 |  |
          * @since 1.5
1227 |  |
          */
1228 |  |
         public void append(Configuration c)
1229 |  |
         {
1230 | 5 |
             if (c != null)
1231 |  |
             {
1232 | 4 |
                 for (Iterator it = c.getKeys(); it.hasNext();)
1233 |  |
                 {
1234 | 49 |
                     String key = (String) it.next();
1235 | 49 |
                     Object value = c.getProperty(key);
1236 | 49 |
                     fireEvent(EVENT_ADD_PROPERTY, key, value, true);
1237 | 49 |
                     addPropertyValues(key, value, DISABLED_DELIMITER);
1238 | 49 |
                     fireEvent(EVENT_ADD_PROPERTY, key, value, false);
1239 | 49 |
                 }
1240 |  |
             }
1241 | 5 |
         }
1242 |  |
1243 |  |
         /**
1244 |  |
          * Returns a configuration with the same content as this configuration, but
1245 |  |
          * with all variables replaced by their actual values. This method tries to
1246 |  |
          * clone the configuration and then perform interpolation on all properties.
1247 |  |
          * So property values of the form <code>${var}</code> will be resolved as
1248 |  |
          * far as possible (if a variable cannot be resolved, it remains unchanged).
1249 |  |
          * This operation is useful if the content of a configuration is to be
1250 |  |
          * exported or processed by an external component that does not support
1251 |  |
          * variable interpolation.
1252 |  |
          *
1253 |  |
          * @return a configuration with all variables interpolated
1254 |  |
          * @throws ConfigurationRuntimeException if this configuration cannot be
1255 |  |
          * cloned
1256 |  |
          * @since 1.5
1257 |  |
          */
1258 |  |
         public Configuration interpolatedConfiguration()
1259 |  |
         {
1260 |  |
             // first clone this configuration
1261 | 1 |
             AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils
1262 |  |
                     .cloneConfiguration(this);
1263 |  |
1264 |  |
             // now perform interpolation
1265 | 1 |
             c.setDelimiterParsingDisabled(true);
1266 | 1 |
             for (Iterator it = getKeys(); it.hasNext();)
1267 |  |
             {
1268 | 8 |
                 String key = (String) it.next();
1269 | 8 |
                 c.setProperty(key, getList(key));
1270 | 8 |
             }
1271 |  |
1272 | 1 |
             c.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
1273 | 1 |
             return c;
1274 |  |
         }
1275 |  |
     }