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

Classes in this File | Line Coverage | Branch Coverage | Complexity
AbstractConfiguration |
96% | 261/273
100% | 56/56
| 2.7058823529411766;2,706
AbstractConfiguration$1 |
100% | 3/3
100% | 1/1
| 2.7058823529411766;2,706
AbstractConfiguration$2 |
33% | 1/3
N/A | N/A
| 2.7058823529411766;2,706
AbstractConfiguration$3 |
100% | 3/3
100% | 1/1
| 2.7058823529411766;2,706

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