001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.configuration2;
019
020import java.math.BigDecimal;
021import java.math.BigInteger;
022import java.time.Duration;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Map;
030import java.util.NoSuchElementException;
031import java.util.Properties;
032import java.util.concurrent.atomic.AtomicReference;
033
034import org.apache.commons.configuration2.convert.ConversionHandler;
035import org.apache.commons.configuration2.convert.DefaultConversionHandler;
036import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
037import org.apache.commons.configuration2.convert.ListDelimiterHandler;
038import org.apache.commons.configuration2.event.BaseEventSource;
039import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
040import org.apache.commons.configuration2.event.ConfigurationEvent;
041import org.apache.commons.configuration2.event.EventListener;
042import org.apache.commons.configuration2.ex.ConversionException;
043import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
044import org.apache.commons.configuration2.interpol.InterpolatorSpecification;
045import org.apache.commons.configuration2.interpol.Lookup;
046import org.apache.commons.configuration2.io.ConfigurationLogger;
047import org.apache.commons.configuration2.sync.LockMode;
048import org.apache.commons.configuration2.sync.NoOpSynchronizer;
049import org.apache.commons.configuration2.sync.Synchronizer;
050import org.apache.commons.lang3.ArrayUtils;
051import org.apache.commons.lang3.ClassUtils;
052import org.apache.commons.lang3.ObjectUtils;
053
054/**
055 * <p>
056 * Abstract configuration class. Provides basic functionality but does not store any data.
057 * </p>
058 * <p>
059 * If you want to write your own Configuration class then you should implement only abstract methods from this class. A
060 * lot of functionality needed by typical implementations of the {@code Configuration} interface is already provided by
061 * this base class. Following is a list of features implemented here:
062 * </p>
063 * <ul>
064 * <li>Data conversion support. The various data types required by the {@code Configuration} interface are already
065 * handled by this base class. A concrete sub class only needs to provide a generic {@code getProperty()} method.</li>
066 * <li>Support for variable interpolation. Property values containing special variable tokens (like {@code ${var}}) will
067 * be replaced by their corresponding values.</li>
068 * <li>Optional support for string lists. The values of properties to be added to this configuration are checked whether
069 * they contain a list delimiter character. If this is the case and if list splitting is enabled, the string is split
070 * and multiple values are added for this property. List splitting is controlled by a {@link ListDelimiterHandler}
071 * object which can be set using the {@link #setListDelimiterHandler(ListDelimiterHandler)} method. It is disabled per
072 * default. To enable this feature, set a suitable {@code ListDelimiterHandler}, e.g. an instance of
073 * {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler DefaultListDelimiterHandler} configured
074 * with the desired list delimiter character.</li>
075 * <li>Allows specifying how missing properties are treated. Per default the get methods returning an object will return
076 * <b>null</b> if the searched property key is not found (and no default value is provided). With the
077 * {@code setThrowExceptionOnMissing()} method this behavior can be changed to throw an exception when a requested
078 * property cannot be found.</li>
079 * <li>Basic event support. Whenever this configuration is modified registered event listeners are notified. Refer to
080 * the various {@code EVENT_XXX} constants to get an impression about which event types are supported.</li>
081 * <li>Support for proper synchronization based on the {@link Synchronizer} interface.</li>
082 * </ul>
083 * <p>
084 * Most methods defined by the {@code Configuration} interface are already implemented in this class. Many method
085 * implementations perform basic book-keeping tasks (e.g. firing events, handling synchronization), and then delegate to
086 * other (protected) methods executing the actual work. Subclasses override these protected methods to define or adapt
087 * behavior. The public entry point methods are final to prevent subclasses from breaking basic functionality.
088 * </p>
089 *
090 */
091public abstract class AbstractConfiguration extends BaseEventSource implements Configuration {
092    /** The list delimiter handler. */
093    private ListDelimiterHandler listDelimiterHandler;
094
095    /** The conversion handler. */
096    private ConversionHandler conversionHandler;
097
098    /**
099     * Whether the configuration should throw NoSuchElementExceptions or simply return null when a property does not exist.
100     * Defaults to return null.
101     */
102    private boolean throwExceptionOnMissing;
103
104    /** Stores a reference to the object that handles variable interpolation. */
105    private AtomicReference<ConfigurationInterpolator> interpolator;
106
107    /** The object responsible for synchronization. */
108    private volatile Synchronizer synchronizer;
109
110    /** The object used for dealing with encoded property values. */
111    private ConfigurationDecoder configurationDecoder;
112
113    /** Stores the logger. */
114    private ConfigurationLogger log;
115
116    /**
117     * Creates a new instance of {@code AbstractConfiguration}.
118     */
119    public AbstractConfiguration() {
120        interpolator = new AtomicReference<>();
121        initLogger(null);
122        installDefaultInterpolator();
123        listDelimiterHandler = DisabledListDelimiterHandler.INSTANCE;
124        conversionHandler = DefaultConversionHandler.INSTANCE;
125    }
126
127    /**
128     * Returns the {@code ListDelimiterHandler} used by this instance.
129     *
130     * @return the {@code ListDelimiterHandler}
131     * @since 2.0
132     */
133    public ListDelimiterHandler getListDelimiterHandler() {
134        return listDelimiterHandler;
135    }
136
137    /**
138     * <p>
139     * Sets the {@code ListDelimiterHandler} to be used by this instance. This object is invoked every time when dealing
140     * with string properties that may contain a list delimiter and thus have to be split to multiple values. Per default, a
141     * {@code ListDelimiterHandler} implementation is set which does not support list splitting. This can be changed for
142     * instance by setting a {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler
143     * DefaultListDelimiterHandler} object.
144     * </p>
145     * <p>
146     * <strong>Warning:</strong> Be careful when changing the list delimiter handler when the configuration has already been
147     * loaded/populated. List handling is typically applied already when properties are added to the configuration. If later
148     * another handler is set which processes lists differently, results may be unexpected; some operations may even cause
149     * exceptions.
150     * </p>
151     *
152     * @param listDelimiterHandler the {@code ListDelimiterHandler} to be used (must not be <b>null</b>)
153     * @throws IllegalArgumentException if the {@code ListDelimiterHandler} is <b>null</b>
154     * @since 2.0
155     */
156    public void setListDelimiterHandler(final ListDelimiterHandler listDelimiterHandler) {
157        if (listDelimiterHandler == null) {
158            throw new IllegalArgumentException("List delimiter handler must not be null!");
159        }
160        this.listDelimiterHandler = listDelimiterHandler;
161    }
162
163    /**
164     * Returns the {@code ConversionHandler} used by this instance.
165     *
166     * @return the {@code ConversionHandler}
167     * @since 2.0
168     */
169    public ConversionHandler getConversionHandler() {
170        return conversionHandler;
171    }
172
173    /**
174     * Sets the {@code ConversionHandler} to be used by this instance. The {@code ConversionHandler} is responsible for
175     * every kind of data type conversion. It is consulted by all get methods returning results in specific data types. A
176     * newly created configuration uses a default {@code ConversionHandler} implementation. This can be changed while
177     * initializing the configuration (e.g. via a builder). Note that access to this property is not synchronized.
178     *
179     * @param conversionHandler the {@code ConversionHandler} to be used (must not be <b>null</b>)
180     * @throws IllegalArgumentException if the {@code ConversionHandler} is <b>null</b>
181     * @since 2.0
182     */
183    public void setConversionHandler(final ConversionHandler conversionHandler) {
184        if (conversionHandler == null) {
185            throw new IllegalArgumentException("ConversionHandler must not be null!");
186        }
187        this.conversionHandler = conversionHandler;
188    }
189
190    /**
191     * Allows to set the {@code throwExceptionOnMissing} flag. This flag controls the behavior of property getter methods
192     * that return objects if the requested property is missing. If the flag is set to <b>false</b> (which is the default
193     * value), these methods will return <b>null</b>. If set to <b>true</b>, they will throw a
194     * {@code NoSuchElementException} exception. Note that getter methods for primitive data types are not affected by this
195     * flag.
196     *
197     * @param throwExceptionOnMissing The new value for the property
198     */
199    public void setThrowExceptionOnMissing(final boolean throwExceptionOnMissing) {
200        this.throwExceptionOnMissing = throwExceptionOnMissing;
201    }
202
203    /**
204     * Returns true if missing values throw Exceptions.
205     *
206     * @return true if missing values throw Exceptions
207     */
208    public boolean isThrowExceptionOnMissing() {
209        return throwExceptionOnMissing;
210    }
211
212    /**
213     * Returns the {@code ConfigurationInterpolator} object that manages the lookup objects for resolving variables.
214     * Unless a custom interpolator has been set or the instance has been modified, the returned interpolator will
215     * resolve values from this configuration instance and support the
216     * {@link ConfigurationInterpolator#getDefaultPrefixLookups() default prefix lookups}.
217     *
218     * @return the {@code ConfigurationInterpolator} associated with this configuration
219     * @since 1.4
220     * @see ConfigurationInterpolator#getDefaultPrefixLookups()
221     */
222    @Override
223    public ConfigurationInterpolator getInterpolator() {
224        return interpolator.get();
225    }
226
227    /**
228     * {@inheritDoc} This implementation sets the passed in object without further modifications. A <b>null</b> argument is
229     * allowed; this disables interpolation.
230     *
231     * @since 2.0
232     */
233    @Override
234    public final void setInterpolator(final ConfigurationInterpolator ci) {
235        interpolator.set(ci);
236    }
237
238    /**
239     * {@inheritDoc} This implementation creates a new {@code ConfigurationInterpolator} instance and initializes it with
240     * the given {@code Lookup} objects. In addition, it adds a specialized default {@code Lookup} object which queries this
241     * {@code Configuration}.
242     *
243     * @since 2.0
244     */
245    @Override
246    public final void installInterpolator(final Map<String, ? extends Lookup> prefixLookups, final Collection<? extends Lookup> defLookups) {
247        final InterpolatorSpecification spec = new InterpolatorSpecification.Builder().withPrefixLookups(prefixLookups).withDefaultLookups(defLookups)
248            .withDefaultLookup(new ConfigurationLookup(this)).create();
249        setInterpolator(ConfigurationInterpolator.fromSpecification(spec));
250    }
251
252    /**
253     * Registers all {@code Lookup} objects in the given map at the current {@code ConfigurationInterpolator} of this
254     * configuration. The set of default lookup objects (for variables without a prefix) is not modified by this method. If
255     * this configuration does not have a {@code ConfigurationInterpolator}, a new instance is created. Note: This method is
256     * mainly intended to be used for initializing a configuration when it is created by a builder. Normal client code
257     * should better call {@link #installInterpolator(Map, Collection)} to define the {@code ConfigurationInterpolator} in a
258     * single step.
259     *
260     * @param lookups a map with new {@code Lookup} objects and their prefixes (may be <b>null</b>)
261     * @since 2.0
262     */
263    public void setPrefixLookups(final Map<String, ? extends Lookup> lookups) {
264        boolean success;
265        do {
266            // do this in a loop because the ConfigurationInterpolator
267            // instance may be changed by another thread
268            final ConfigurationInterpolator ciOld = getInterpolator();
269            final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator();
270            ciNew.registerLookups(lookups);
271            success = interpolator.compareAndSet(ciOld, ciNew);
272        } while (!success);
273    }
274
275    /**
276     * Adds all {@code Lookup} objects in the given collection as default lookups (i.e. lookups without a variable prefix)
277     * to the {@code ConfigurationInterpolator} object of this configuration. In addition, it adds a specialized default
278     * {@code Lookup} object which queries this {@code Configuration}. The set of {@code Lookup} objects with prefixes is
279     * not modified by this method. If this configuration does not have a {@code ConfigurationInterpolator}, a new instance
280     * is created. Note: This method is mainly intended to be used for initializing a configuration when it is created by a
281     * builder. Normal client code should better call {@link #installInterpolator(Map, Collection)} to define the
282     * {@code ConfigurationInterpolator} in a single step.
283     *
284     * @param lookups the collection with default {@code Lookup} objects to be added
285     * @since 2.0
286     */
287    public void setDefaultLookups(final Collection<? extends Lookup> lookups) {
288        boolean success;
289        do {
290            final ConfigurationInterpolator ciOld = getInterpolator();
291            final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator();
292            Lookup confLookup = findConfigurationLookup(ciNew);
293            if (confLookup == null) {
294                confLookup = new ConfigurationLookup(this);
295            } else {
296                ciNew.removeDefaultLookup(confLookup);
297            }
298            ciNew.addDefaultLookups(lookups);
299            ciNew.addDefaultLookup(confLookup);
300            success = interpolator.compareAndSet(ciOld, ciNew);
301        } while (!success);
302    }
303
304    /**
305     * Sets the specified {@code ConfigurationInterpolator} as the parent of this configuration's
306     * {@code ConfigurationInterpolator}. If this configuration does not have a {@code ConfigurationInterpolator}, a new
307     * instance is created. Note: This method is mainly intended to be used for initializing a configuration when it is
308     * created by a builder. Normal client code can directly update the {@code ConfigurationInterpolator}.
309     *
310     * @param parent the parent {@code ConfigurationInterpolator} to be set
311     * @since 2.0
312     */
313    public void setParentInterpolator(final ConfigurationInterpolator parent) {
314        boolean success;
315        do {
316            final ConfigurationInterpolator ciOld = getInterpolator();
317            final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator();
318            ciNew.setParentInterpolator(parent);
319            success = interpolator.compareAndSet(ciOld, ciNew);
320        } while (!success);
321    }
322
323    /**
324     * Sets the {@code ConfigurationDecoder} for this configuration. This object is used by
325     * {@link #getEncodedString(String)}.
326     *
327     * @param configurationDecoder the {@code ConfigurationDecoder}
328     * @since 2.0
329     */
330    public void setConfigurationDecoder(final ConfigurationDecoder configurationDecoder) {
331        this.configurationDecoder = configurationDecoder;
332    }
333
334    /**
335     * Returns the {@code ConfigurationDecoder} used by this instance.
336     *
337     * @return the {@code ConfigurationDecoder}
338     * @since 2.0
339     */
340    public ConfigurationDecoder getConfigurationDecoder() {
341        return configurationDecoder;
342    }
343
344    /**
345     * Creates a clone of the {@code ConfigurationInterpolator} used by this instance. This method can be called by
346     * {@code clone()} implementations of derived classes. Normally, the {@code ConfigurationInterpolator} of a
347     * configuration instance must not be shared with other instances because it contains a specific {@code Lookup} object
348     * pointing to the owning configuration. This has to be taken into account when cloning a configuration. This method
349     * creates a new {@code ConfigurationInterpolator} for this configuration instance which contains all lookup objects
350     * from the original {@code ConfigurationInterpolator} except for the configuration specific lookup pointing to the
351     * passed in original configuration. This one is replaced by a corresponding {@code Lookup} referring to this
352     * configuration.
353     *
354     * @param orgConfig the original configuration from which this one was cloned
355     * @since 2.0
356     */
357    protected void cloneInterpolator(final AbstractConfiguration orgConfig) {
358        interpolator = new AtomicReference<>();
359        final ConfigurationInterpolator orgInterpolator = orgConfig.getInterpolator();
360        final List<Lookup> defaultLookups = orgInterpolator.getDefaultLookups();
361        final Lookup lookup = findConfigurationLookup(orgInterpolator, orgConfig);
362        if (lookup != null) {
363            defaultLookups.remove(lookup);
364        }
365
366        installInterpolator(orgInterpolator.getLookups(), defaultLookups);
367    }
368
369    /**
370     * Creates a default {@code ConfigurationInterpolator} which is initialized with all default {@code Lookup} objects.
371     * This method is called by the constructor. It ensures that default interpolation works for every new configuration
372     * instance.
373     */
374    private void installDefaultInterpolator() {
375        installInterpolator(ConfigurationInterpolator.getDefaultPrefixLookups(), null);
376    }
377
378    /**
379     * Finds a {@code ConfigurationLookup} pointing to this configuration in the default lookups of the specified
380     * {@code ConfigurationInterpolator}. This method is called to ensure that there is exactly one default lookup querying
381     * this configuration.
382     *
383     * @param ci the {@code ConfigurationInterpolator} in question
384     * @return the found {@code Lookup} object or <b>null</b>
385     */
386    private Lookup findConfigurationLookup(final ConfigurationInterpolator ci) {
387        return findConfigurationLookup(ci, this);
388    }
389
390    /**
391     * Finds a {@code ConfigurationLookup} pointing to the specified configuration in the default lookups for the specified
392     * {@code ConfigurationInterpolator}.
393     *
394     * @param ci the {@code ConfigurationInterpolator} in question
395     * @param targetConf the target configuration of the searched lookup
396     * @return the found {@code Lookup} object or <b>null</b>
397     */
398    private static Lookup findConfigurationLookup(final ConfigurationInterpolator ci, final ImmutableConfiguration targetConf) {
399        for (final Lookup l : ci.getDefaultLookups()) {
400            if (l instanceof ConfigurationLookup && targetConf == ((ConfigurationLookup) l).getConfiguration()) {
401                return l;
402            }
403        }
404        return null;
405    }
406
407    /**
408     * Returns the logger used by this configuration object.
409     *
410     * @return the logger
411     * @since 2.0
412     */
413    public ConfigurationLogger getLogger() {
414        return log;
415    }
416
417    /**
418     * Allows setting the logger to be used by this configuration object. This method makes it possible for clients to
419     * exactly control logging behavior. Per default a logger is set that will ignore all log messages. Derived classes that
420     * want to enable logging should call this method during their initialization with the logger to be used. It is legal to
421     * pass a <b>null</b> logger; in this case, logging will be disabled.
422     *
423     * @param log the new logger
424     * @since 2.0
425     */
426    public void setLogger(final ConfigurationLogger log) {
427        initLogger(log);
428    }
429
430    /**
431     * Adds a special {@link EventListener} object to this configuration that will log all internal errors. This method is
432     * intended to be used by certain derived classes, for which it is known that they can fail on property access (e.g.
433     * {@code DatabaseConfiguration}).
434     *
435     * @since 1.4
436     */
437    public final void addErrorLogListener() {
438        addEventListener(ConfigurationErrorEvent.ANY, event -> getLogger().warn("Internal error", event.getCause()));
439    }
440
441    /**
442     * Returns the object responsible for synchronizing this configuration. All access to this configuration - both read and
443     * write access - is controlled by this object. This implementation never returns <b>null</b>. If no
444     * {@code Synchronizer} has been set, a {@link NoOpSynchronizer} is returned. So, per default, instances of
445     * {@code AbstractConfiguration} are not thread-safe unless a suitable {@code Synchronizer} is set!
446     *
447     * @return the {@code Synchronizer} used by this instance
448     * @since 2.0
449     */
450    @Override
451    public final Synchronizer getSynchronizer() {
452        final Synchronizer sync = synchronizer;
453        return sync != null ? sync : NoOpSynchronizer.INSTANCE;
454    }
455
456    /**
457     * Sets the object responsible for synchronizing this configuration. This method has to be called with a suitable
458     * {@code Synchronizer} object when initializing this configuration instance in order to make it thread-safe.
459     *
460     * @param synchronizer the new {@code Synchronizer}; can be <b>null</b>, then this instance uses a
461     *        {@link NoOpSynchronizer}
462     * @since 2.0
463     */
464    @Override
465    public final void setSynchronizer(final Synchronizer synchronizer) {
466        this.synchronizer = synchronizer;
467    }
468
469    /**
470     * {@inheritDoc} This implementation delegates to {@code beginRead()} or {@code beginWrite()}, depending on the
471     * {@code LockMode} argument. Subclasses can override these protected methods to perform additional steps when a
472     * configuration is locked.
473     *
474     * @since 2.0
475     * @throws NullPointerException if the argument is <b>null</b>
476     */
477    @Override
478    public final void lock(final LockMode mode) {
479        switch (mode) {
480        case READ:
481            beginRead(false);
482            break;
483        case WRITE:
484            beginWrite(false);
485            break;
486        default:
487            throw new IllegalArgumentException("Unsupported LockMode: " + mode);
488        }
489    }
490
491    /**
492     * {@inheritDoc} This implementation delegates to {@code endRead()} or {@code endWrite()}, depending on the
493     * {@code LockMode} argument. Subclasses can override these protected methods to perform additional steps when a
494     * configuration's lock is released.
495     *
496     * @throws NullPointerException if the argument is <b>null</b>
497     */
498    @Override
499    public final void unlock(final LockMode mode) {
500        switch (mode) {
501        case READ:
502            endRead();
503            break;
504        case WRITE:
505            endWrite();
506            break;
507        default:
508            throw new IllegalArgumentException("Unsupported LockMode: " + mode);
509        }
510    }
511
512    /**
513     * Notifies this configuration's {@link Synchronizer} that a read operation is about to start. This method is called by
514     * all methods which access this configuration in a read-only mode. Subclasses may override it to perform additional
515     * actions before this read operation. The boolean <em>optimize</em> argument can be evaluated by overridden methods in
516     * derived classes. Some operations which require a lock do not need a fully initialized configuration object. By
517     * setting this flag to <strong>true</strong>, such operations can give a corresponding hint. An overridden
518     * implementation of {@code beginRead()} can then decide to skip some initialization steps. All basic operations in this
519     * class (and most of the basic {@code Configuration} implementations) call this method with a parameter value of
520     * <strong>false</strong>. <strong>In any case the inherited method must be called! Otherwise, proper synchronization is
521     * not guaranteed.</strong>
522     *
523     * @param optimize a flag whether optimization can be performed
524     * @since 2.0
525     */
526    protected void beginRead(final boolean optimize) {
527        getSynchronizer().beginRead();
528    }
529
530    /**
531     * Notifies this configuration's {@link Synchronizer} that a read operation has finished. This method is called by all
532     * methods which access this configuration in a read-only manner at the end of their execution. Subclasses may override
533     * it to perform additional actions after this read operation. <strong>In any case the inherited method must be called!
534     * Otherwise, the read lock will not be released.</strong>
535     *
536     * @since 2.0
537     */
538    protected void endRead() {
539        getSynchronizer().endRead();
540    }
541
542    /**
543     * Notifies this configuration's {@link Synchronizer} that an update operation is about to start. This method is called
544     * by all methods which modify this configuration. Subclasses may override it to perform additional operations before an
545     * update. For a description of the boolean <em>optimize</em> argument refer to the documentation of
546     * {@code beginRead()}. <strong>In any case the inherited method must be called! Otherwise, proper synchronization is
547     * not guaranteed.</strong>
548     *
549     * @param optimize a flag whether optimization can be performed
550     * @see #beginRead(boolean)
551     * @since 2.0
552     */
553    protected void beginWrite(final boolean optimize) {
554        getSynchronizer().beginWrite();
555    }
556
557    /**
558     * Notifies this configuration's {@link Synchronizer} that an update operation has finished. This method is called by
559     * all methods which modify this configuration at the end of their execution. Subclasses may override it to perform
560     * additional operations after an update. <strong>In any case the inherited method must be called! Otherwise, the write
561     * lock will not be released.</strong>
562     *
563     * @since 2.0
564     */
565    protected void endWrite() {
566        getSynchronizer().endWrite();
567    }
568
569    @Override
570    public final void addProperty(final String key, final Object value) {
571        beginWrite(false);
572        try {
573            fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, true);
574            addPropertyInternal(key, value);
575            fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, false);
576        } finally {
577            endWrite();
578        }
579    }
580
581    /**
582     * Actually adds a property to this configuration. This method is called by {@code addProperty()}. It performs list
583     * splitting if necessary and delegates to {@link #addPropertyDirect(String, Object)} for every single property value.
584     *
585     * @param key the key of the property to be added
586     * @param value the new property value
587     * @since 2.0
588     */
589    protected void addPropertyInternal(final String key, final Object value) {
590        for (final Object obj : getListDelimiterHandler().parse(value)) {
591            addPropertyDirect(key, obj);
592        }
593    }
594
595    /**
596     * Adds a key/value pair to the Configuration. Override this method to provide write access to underlying Configuration
597     * store.
598     *
599     * @param key key to use for mapping
600     * @param value object to store
601     */
602    protected abstract void addPropertyDirect(String key, Object value);
603
604    /**
605     * interpolate key names to handle ${key} stuff
606     *
607     * @param base string to interpolate
608     *
609     * @return returns the key name with the ${key} substituted
610     */
611    protected String interpolate(final String base) {
612        final Object result = interpolate((Object) base);
613        return result == null ? null : result.toString();
614    }
615
616    /**
617     * Returns the interpolated value. This implementation delegates to the current {@code ConfigurationInterpolator}. If no
618     * {@code ConfigurationInterpolator} is set, the passed in value is returned without changes.
619     *
620     * @param value the value to interpolate
621     * @return the value with variables substituted
622     */
623    protected Object interpolate(final Object value) {
624        final ConfigurationInterpolator ci = getInterpolator();
625        return ci != null ? ci.interpolate(value) : value;
626    }
627
628    @Override
629    public Configuration subset(final String prefix) {
630        return new SubsetConfiguration(this, prefix, ".");
631    }
632
633    @Override
634    public ImmutableConfiguration immutableSubset(final String prefix) {
635        return ConfigurationUtils.unmodifiableConfiguration(subset(prefix));
636    }
637
638    @Override
639    public final void setProperty(final String key, final Object value) {
640        beginWrite(false);
641        try {
642            fireEvent(ConfigurationEvent.SET_PROPERTY, key, value, true);
643            setPropertyInternal(key, value);
644            fireEvent(ConfigurationEvent.SET_PROPERTY, key, value, false);
645        } finally {
646            endWrite();
647        }
648    }
649
650    /**
651     * Actually sets the value of a property. This method is called by {@code setProperty()}. It provides a default
652     * implementation of this functionality by clearing the specified key and delegating to {@code addProperty()}.
653     * Subclasses should override this method if they can provide a more efficient algorithm for setting a property value.
654     *
655     * @param key the property key
656     * @param value the new property value
657     * @since 2.0
658     */
659    protected void setPropertyInternal(final String key, final Object value) {
660        setDetailEvents(false);
661        try {
662            clearProperty(key);
663            addProperty(key, value);
664        } finally {
665            setDetailEvents(true);
666        }
667    }
668
669    /**
670     * Removes the specified property from this configuration. This implementation performs some preparations and then
671     * delegates to {@code clearPropertyDirect()}, which will do the real work.
672     *
673     * @param key the key to be removed
674     */
675    @Override
676    public final void clearProperty(final String key) {
677        beginWrite(false);
678        try {
679            fireEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, true);
680            clearPropertyDirect(key);
681            fireEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, false);
682        } finally {
683            endWrite();
684        }
685    }
686
687    /**
688     * Removes the specified property from this configuration. This method is called by {@code clearProperty()} after it has
689     * done some preparations. It must be overridden in sub classes.
690     *
691     * @param key the key to be removed
692     */
693    protected abstract void clearPropertyDirect(String key);
694
695    @Override
696    public final void clear() {
697        beginWrite(false);
698        try {
699            fireEvent(ConfigurationEvent.CLEAR, null, null, true);
700            clearInternal();
701            fireEvent(ConfigurationEvent.CLEAR, null, null, false);
702        } finally {
703            endWrite();
704        }
705    }
706
707    /**
708     * Clears the whole configuration. This method is called by {@code clear()} after some preparations have been made. This
709     * base implementation uses the iterator provided by {@code getKeys()} to remove every single property. Subclasses
710     * should override this method if there is a more efficient way of clearing the configuration.
711     */
712    protected void clearInternal() {
713        setDetailEvents(false);
714        boolean useIterator = true;
715        try {
716            final Iterator<String> it = getKeys();
717            while (it.hasNext()) {
718                final String key = it.next();
719                if (useIterator) {
720                    try {
721                        it.remove();
722                    } catch (final UnsupportedOperationException usoex) {
723                        useIterator = false;
724                    }
725                }
726
727                if (useIterator && containsKey(key)) {
728                    useIterator = false;
729                }
730
731                if (!useIterator) {
732                    // workaround for Iterators that do not remove the
733                    // property
734                    // on calling remove() or do not support remove() at all
735                    clearProperty(key);
736                }
737            }
738        } finally {
739            setDetailEvents(true);
740        }
741    }
742
743    /**
744     * {@inheritDoc} This implementation takes care of synchronization and then delegates to {@code getKeysInternal()} for
745     * obtaining the actual iterator. Note that depending on a concrete implementation, an iteration may fail if the
746     * configuration is updated concurrently.
747     */
748    @Override
749    public final Iterator<String> getKeys() {
750        beginRead(false);
751        try {
752            return getKeysInternal();
753        } finally {
754            endRead();
755        }
756    }
757
758    /**
759     * {@inheritDoc} This implementation returns keys that either match the prefix or start with the prefix followed by a
760     * dot ('.'). So the call {@code getKeys("db");} will find the keys {@code db}, {@code db.user}, or {@code db.password},
761     * but not the key {@code dbdriver}.
762     */
763    @Override
764    public final Iterator<String> getKeys(final String prefix) {
765        beginRead(false);
766        try {
767            return getKeysInternal(prefix);
768        } finally {
769            endRead();
770        }
771    }
772
773    /**
774     * Actually creates an iterator for iterating over the keys in this configuration. This method is called by
775     * {@code getKeys()}, it has to be defined by concrete subclasses.
776     *
777     * @return an {@code Iterator} with all property keys in this configuration
778     * @since 2.0
779     */
780    protected abstract Iterator<String> getKeysInternal();
781
782    /**
783     * Returns an {@code Iterator} with all property keys starting with the specified prefix. This method is called by
784     * {@link #getKeys(String)}. It is fully implemented by delegating to {@code getKeysInternal()} and returning a special
785     * iterator which filters for the passed in prefix. Subclasses can override it if they can provide a more efficient way
786     * to iterate over specific keys only.
787     *
788     * @param prefix the prefix for the keys to be taken into account
789     * @return an {@code Iterator} returning the filtered keys
790     * @since 2.0
791     */
792    protected Iterator<String> getKeysInternal(final String prefix) {
793        return new PrefixedKeysIterator(getKeysInternal(), prefix);
794    }
795
796    /**
797     * {@inheritDoc} This implementation ensures proper synchronization. Subclasses have to define the abstract
798     * {@code getPropertyInternal()} method which is called from here.
799     */
800    @Override
801    public final Object getProperty(final String key) {
802        beginRead(false);
803        try {
804            return getPropertyInternal(key);
805        } finally {
806            endRead();
807        }
808    }
809
810    /**
811     * Actually obtains the value of the specified property. This method is called by {@code getProperty()}. Concrete
812     * subclasses must define it to fetch the value of the desired property.
813     *
814     * @param key the key of the property in question
815     * @return the (raw) value of this property
816     * @since 2.0
817     */
818    protected abstract Object getPropertyInternal(String key);
819
820    /**
821     * {@inheritDoc} This implementation handles synchronization and delegates to {@code isEmptyInternal()}.
822     */
823    @Override
824    public final boolean isEmpty() {
825        beginRead(false);
826        try {
827            return isEmptyInternal();
828        } finally {
829            endRead();
830        }
831    }
832
833    /**
834     * Actually checks whether this configuration contains data. This method is called by {@code isEmpty()}. It has to be
835     * defined by concrete subclasses.
836     *
837     * @return <b>true</b> if this configuration contains no data, <b>false</b> otherwise
838     * @since 2.0
839     */
840    protected abstract boolean isEmptyInternal();
841
842    /**
843     * {@inheritDoc} This implementation handles synchronization and delegates to {@code sizeInternal()}.
844     */
845    @Override
846    public final int size() {
847        beginRead(false);
848        try {
849            return sizeInternal();
850        } finally {
851            endRead();
852        }
853    }
854
855    /**
856     * Actually calculates the size of this configuration. This method is called by {@code size()} with a read lock held.
857     * The base implementation provided here calculates the size based on the iterator returned by {@code getKeys()}. Sub
858     * classes which can determine the size in a more efficient way should override this method.
859     *
860     * @return the size of this configuration (i.e. the number of keys)
861     */
862    protected int sizeInternal() {
863        int size = 0;
864        for (final Iterator<String> keyIt = getKeysInternal(); keyIt.hasNext(); size++) {
865            keyIt.next();
866        }
867        return size;
868    }
869
870    /**
871     * {@inheritDoc} This implementation handles synchronization and delegates to {@code containsKeyInternal()}.
872     */
873    @Override
874    public final boolean containsKey(final String key) {
875        beginRead(false);
876        try {
877            return containsKeyInternal(key);
878        } finally {
879            endRead();
880        }
881    }
882
883    /**
884     * Actually checks whether the specified key is contained in this configuration. This method is called by
885     * {@code containsKey()}. It has to be defined by concrete subclasses.
886     *
887     * @param key the key in question
888     * @return <b>true</b> if this key is contained in this configuration, <b>false</b> otherwise
889     * @since 2.0
890     */
891    protected abstract boolean containsKeyInternal(String key);
892
893    @Override
894    public Properties getProperties(final String key) {
895        return getProperties(key, null);
896    }
897
898    /**
899     * Get a list of properties associated with the given configuration key.
900     *
901     * @param key The configuration key.
902     * @param defaults Any default values for the returned {@code Properties} object. Ignored if {@code null}.
903     *
904     * @return The associated properties if key is found.
905     *
906     * @throws ConversionException is thrown if the key maps to an object that is not a String/List of Strings.
907     *
908     * @throws IllegalArgumentException if one of the tokens is malformed (does not contain an equals sign).
909     */
910    public Properties getProperties(final String key, final Properties defaults) {
911        /*
912         * Grab an array of the tokens for this key.
913         */
914        final String[] tokens = getStringArray(key);
915
916        /*
917         * Each token is of the form 'key=value'.
918         */
919        final Properties props = defaults == null ? new Properties() : new Properties(defaults);
920        for (final String token : tokens) {
921            final int equalSign = token.indexOf('=');
922            if (equalSign > 0) {
923                final String pkey = token.substring(0, equalSign).trim();
924                final String pvalue = token.substring(equalSign + 1).trim();
925                props.put(pkey, pvalue);
926            } else if (tokens.length == 1 && "".equals(token)) {
927                // Semantically equivalent to an empty Properties
928                // object.
929                break;
930            } else {
931                throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign");
932            }
933        }
934        return props;
935    }
936
937    @Override
938    public boolean getBoolean(final String key) {
939        final Boolean b = convert(Boolean.class, key, null, true);
940        return checkNonNullValue(key, b).booleanValue();
941    }
942
943    @Override
944    public boolean getBoolean(final String key, final boolean defaultValue) {
945        return getBoolean(key, Boolean.valueOf(defaultValue)).booleanValue();
946    }
947
948    /**
949     * Obtains the value of the specified key and tries to convert it into a {@code Boolean} object. If the property has no
950     * value, the passed in default value will be used.
951     *
952     * @param key the key of the property
953     * @param defaultValue the default value
954     * @return the value of this key converted to a {@code Boolean}
955     * @throws ConversionException if the value cannot be converted to a {@code Boolean}
956     */
957    @Override
958    public Boolean getBoolean(final String key, final Boolean defaultValue) {
959        return convert(Boolean.class, key, defaultValue, false);
960    }
961
962    @Override
963    public byte getByte(final String key) {
964        final Byte b = convert(Byte.class, key, null, true);
965        return checkNonNullValue(key, b).byteValue();
966    }
967
968    @Override
969    public byte getByte(final String key, final byte defaultValue) {
970        return getByte(key, Byte.valueOf(defaultValue)).byteValue();
971    }
972
973    @Override
974    public Byte getByte(final String key, final Byte defaultValue) {
975        return convert(Byte.class, key, defaultValue, false);
976    }
977
978    @Override
979    public double getDouble(final String key) {
980        final Double d = convert(Double.class, key, null, true);
981        return checkNonNullValue(key, d).doubleValue();
982    }
983
984    @Override
985    public double getDouble(final String key, final double defaultValue) {
986        return getDouble(key, Double.valueOf(defaultValue)).doubleValue();
987    }
988
989    @Override
990    public Double getDouble(final String key, final Double defaultValue) {
991        return convert(Double.class, key, defaultValue, false);
992    }
993
994    @Override
995    public Duration getDuration(final String key) {
996        return checkNonNullValue(key, convert(Duration.class, key, null, true));
997    }
998
999    @Override
1000    public Duration getDuration(final String key, final Duration defaultValue) {
1001        return convert(Duration.class, key, defaultValue, false);
1002    }
1003
1004    @Override
1005    public float getFloat(final String key) {
1006        final Float f = convert(Float.class, key, null, true);
1007        return checkNonNullValue(key, f).floatValue();
1008    }
1009
1010    @Override
1011    public float getFloat(final String key, final float defaultValue) {
1012        return getFloat(key, Float.valueOf(defaultValue)).floatValue();
1013    }
1014
1015    @Override
1016    public Float getFloat(final String key, final Float defaultValue) {
1017        return convert(Float.class, key, defaultValue, false);
1018    }
1019
1020    @Override
1021    public int getInt(final String key) {
1022        final Integer i = convert(Integer.class, key, null, true);
1023        return checkNonNullValue(key, i).intValue();
1024    }
1025
1026    @Override
1027    public int getInt(final String key, final int defaultValue) {
1028        return getInteger(key, Integer.valueOf(defaultValue)).intValue();
1029    }
1030
1031    @Override
1032    public Integer getInteger(final String key, final Integer defaultValue) {
1033        return convert(Integer.class, key, defaultValue, false);
1034    }
1035
1036    @Override
1037    public long getLong(final String key) {
1038        final Long l = convert(Long.class, key, null, true);
1039        return checkNonNullValue(key, l).longValue();
1040    }
1041
1042    @Override
1043    public long getLong(final String key, final long defaultValue) {
1044        return getLong(key, Long.valueOf(defaultValue)).longValue();
1045    }
1046
1047    @Override
1048    public Long getLong(final String key, final Long defaultValue) {
1049        return convert(Long.class, key, defaultValue, false);
1050    }
1051
1052    @Override
1053    public short getShort(final String key) {
1054        final Short s = convert(Short.class, key, null, true);
1055        return checkNonNullValue(key, s).shortValue();
1056    }
1057
1058    @Override
1059    public short getShort(final String key, final short defaultValue) {
1060        return getShort(key, Short.valueOf(defaultValue)).shortValue();
1061    }
1062
1063    @Override
1064    public Short getShort(final String key, final Short defaultValue) {
1065        return convert(Short.class, key, defaultValue, false);
1066    }
1067
1068    /**
1069     * {@inheritDoc}
1070     *
1071     * @see #setThrowExceptionOnMissing(boolean)
1072     */
1073    @Override
1074    public BigDecimal getBigDecimal(final String key) {
1075        return convert(BigDecimal.class, key, null, true);
1076    }
1077
1078    @Override
1079    public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) {
1080        return convert(BigDecimal.class, key, defaultValue, false);
1081    }
1082
1083    /**
1084     * {@inheritDoc}
1085     *
1086     * @see #setThrowExceptionOnMissing(boolean)
1087     */
1088    @Override
1089    public BigInteger getBigInteger(final String key) {
1090        return convert(BigInteger.class, key, null, true);
1091    }
1092
1093    @Override
1094    public BigInteger getBigInteger(final String key, final BigInteger defaultValue) {
1095        return convert(BigInteger.class, key, defaultValue, false);
1096    }
1097
1098    /**
1099     * {@inheritDoc}
1100     *
1101     * @see #setThrowExceptionOnMissing(boolean)
1102     */
1103    @Override
1104    public String getString(final String key) {
1105        return convert(String.class, key, null, true);
1106    }
1107
1108    @Override
1109    public String getString(final String key, final String defaultValue) {
1110        final String result = convert(String.class, key, null, false);
1111        return result != null ? result : interpolate(defaultValue);
1112    }
1113
1114    /**
1115     * {@inheritDoc} This implementation delegates to {@link #getString(String)} in order to obtain the value of the passed
1116     * in key. This value is passed to the decoder. Because {@code getString()} is used behind the scenes all standard
1117     * features like handling of missing keys and interpolation work as expected.
1118     */
1119    @Override
1120    public String getEncodedString(final String key, final ConfigurationDecoder decoder) {
1121        if (decoder == null) {
1122            throw new IllegalArgumentException("ConfigurationDecoder must not be null!");
1123        }
1124
1125        final String value = getString(key);
1126        return value != null ? decoder.decode(value) : null;
1127    }
1128
1129    /**
1130     * {@inheritDoc} This implementation makes use of the {@code ConfigurationDecoder} set for this configuration. If no
1131     * such object has been set, an {@code IllegalStateException} exception is thrown.
1132     *
1133     * @throws IllegalStateException if no {@code ConfigurationDecoder} is set
1134     * @see #setConfigurationDecoder(ConfigurationDecoder)
1135     */
1136    @Override
1137    public String getEncodedString(final String key) {
1138        final ConfigurationDecoder decoder = getConfigurationDecoder();
1139        if (decoder == null) {
1140            throw new IllegalStateException("No default ConfigurationDecoder defined!");
1141        }
1142        return getEncodedString(key, decoder);
1143    }
1144
1145    /**
1146     * Get an array of strings associated with the given configuration key. If the key doesn't map to an existing object, an
1147     * empty array is returned. When a property is added to a configuration, it is checked whether it contains multiple
1148     * values. This is obvious if the added object is a list or an array. For strings the association
1149     * {@link ListDelimiterHandler} is consulted to find out whether the string can be split into multiple values.
1150     *
1151     * @param key The configuration key.
1152     * @return The associated string array if key is found.
1153     *
1154     * @throws ConversionException is thrown if the key maps to an object that is not a String/List of Strings.
1155     * @see #setListDelimiterHandler(ListDelimiterHandler)
1156     */
1157    @Override
1158    public String[] getStringArray(final String key) {
1159        final String[] result = (String[]) getArray(String.class, key);
1160        return result == null ? ArrayUtils.EMPTY_STRING_ARRAY : result;
1161    }
1162
1163    /**
1164     * {@inheritDoc}
1165     *
1166     * @see #getStringArray(String)
1167     */
1168    @Override
1169    public List<Object> getList(final String key) {
1170        return getList(key, new ArrayList<>());
1171    }
1172
1173    @Override
1174    public List<Object> getList(final String key, final List<?> defaultValue) {
1175        final Object value = getProperty(key);
1176        final List<Object> list;
1177
1178        if (value instanceof String) {
1179            list = new ArrayList<>(1);
1180            list.add(interpolate((String) value));
1181        } else if (value instanceof List) {
1182            list = new ArrayList<>();
1183            final List<?> l = (List<?>) value;
1184
1185            // add the interpolated elements in the new list
1186            for (final Object elem : l) {
1187                list.add(interpolate(elem));
1188            }
1189        } else if (value == null) {
1190            // This is okay because we just return this list to the caller
1191            @SuppressWarnings("unchecked")
1192            final List<Object> resultList = (List<Object>) defaultValue;
1193            list = resultList;
1194        } else if (value.getClass().isArray()) {
1195            return Arrays.asList((Object[]) value);
1196        } else if (isScalarValue(value)) {
1197            return Collections.singletonList((Object) value.toString());
1198        } else {
1199            throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " + value.getClass().getName());
1200        }
1201        return list;
1202    }
1203
1204    @Override
1205    public <T> T get(final Class<T> cls, final String key) {
1206        return convert(cls, key, null, true);
1207    }
1208
1209    /**
1210     * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion.
1211     */
1212    @Override
1213    public <T> T get(final Class<T> cls, final String key, final T defaultValue) {
1214        return convert(cls, key, defaultValue, false);
1215    }
1216
1217    @Override
1218    public Object getArray(final Class<?> cls, final String key) {
1219        return getArray(cls, key, null);
1220    }
1221
1222    /**
1223     * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion.
1224     * If this results in a <b>null</b> result (because the property is undefined), the default value is returned. It is
1225     * checked whether the default value is an array with the correct component type. If not, an exception is thrown.
1226     *
1227     * @throws IllegalArgumentException if the default value is not a compatible array
1228     */
1229    @Override
1230    public Object getArray(final Class<?> cls, final String key, final Object defaultValue) {
1231        return convertToArray(cls, key, defaultValue);
1232    }
1233
1234    @Override
1235    public <T> List<T> getList(final Class<T> cls, final String key) {
1236        return getList(cls, key, null);
1237    }
1238
1239    /**
1240     * {@inheritDoc} This implementation delegates to the generic {@code getCollection()}. As target collection a newly
1241     * created {@code ArrayList} is passed in.
1242     */
1243    @Override
1244    public <T> List<T> getList(final Class<T> cls, final String key, final List<T> defaultValue) {
1245        final List<T> result = new ArrayList<>();
1246        if (getCollection(cls, key, result, defaultValue) == null) {
1247            return null;
1248        }
1249        return result;
1250    }
1251
1252    @Override
1253    public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target) {
1254        return getCollection(cls, key, target, null);
1255    }
1256
1257    /**
1258     * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual conversion. If no
1259     * target collection is provided, an {@code ArrayList} is created.
1260     */
1261    @Override
1262    public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target, final Collection<T> defaultValue) {
1263        final Object src = getProperty(key);
1264        if (src == null) {
1265            return handleDefaultCollection(target, defaultValue);
1266        }
1267
1268        final Collection<T> targetCol = target != null ? target : new ArrayList<>();
1269        getConversionHandler().toCollection(src, cls, getInterpolator(), targetCol);
1270        return targetCol;
1271    }
1272
1273    /**
1274     * Checks whether the specified object is a scalar value. This method is called by {@code getList()} and
1275     * {@code getStringArray()} if the property requested is not a string, a list, or an array. If it returns <b>true</b>,
1276     * the calling method transforms the value to a string and returns a list or an array with this single element. This
1277     * implementation returns <b>true</b> if the value is of a wrapper type for a primitive type.
1278     *
1279     * @param value the value to be checked
1280     * @return a flag whether the value is a scalar
1281     * @since 1.7
1282     */
1283    protected boolean isScalarValue(final Object value) {
1284        return ClassUtils.wrapperToPrimitive(value.getClass()) != null;
1285    }
1286
1287    /**
1288     * Copies the content of the specified configuration into this configuration. If the specified configuration contains a
1289     * key that is also present in this configuration, the value of this key will be replaced by the new value.
1290     * <em>Note:</em> This method won't work well when copying hierarchical configurations because it is not able to copy
1291     * information about the properties' structure (i.e. the parent-child-relationships will get lost). So when dealing with
1292     * hierarchical configuration objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be
1293     * used.
1294     *
1295     * @param c the configuration to copy (can be <b>null</b>, then this operation will have no effect)
1296     * @since 1.5
1297     */
1298    public void copy(final Configuration c) {
1299        if (c != null) {
1300            c.lock(LockMode.READ);
1301            try {
1302                for (final Iterator<String> it = c.getKeys(); it.hasNext();) {
1303                    final String key = it.next();
1304                    final Object value = encodeForCopy(c.getProperty(key));
1305                    setProperty(key, value);
1306                }
1307            } finally {
1308                c.unlock(LockMode.READ);
1309            }
1310        }
1311    }
1312
1313    /**
1314     * Appends the content of the specified configuration to this configuration. The values of all properties contained in
1315     * the specified configuration will be appended to this configuration. So if a property is already present in this
1316     * configuration, its new value will be a union of the values in both configurations. <em>Note:</em> This method won't
1317     * work well when appending hierarchical configurations because it is not able to copy information about the properties'
1318     * structure (i.e. the parent-child-relationships will get lost). So when dealing with hierarchical configuration
1319     * objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be used.
1320     *
1321     * @param c the configuration to be appended (can be <b>null</b>, then this operation will have no effect)
1322     * @since 1.5
1323     */
1324    public void append(final Configuration c) {
1325        if (c != null) {
1326            c.lock(LockMode.READ);
1327            try {
1328                for (final Iterator<String> it = c.getKeys(); it.hasNext();) {
1329                    final String key = it.next();
1330                    final Object value = encodeForCopy(c.getProperty(key));
1331                    addProperty(key, value);
1332                }
1333            } finally {
1334                c.unlock(LockMode.READ);
1335            }
1336        }
1337    }
1338
1339    /**
1340     * Returns a configuration with the same content as this configuration, but with all variables replaced by their actual
1341     * values. This method tries to clone the configuration and then perform interpolation on all properties. So property
1342     * values of the form {@code ${var}} will be resolved as far as possible (if a variable cannot be resolved, it remains
1343     * unchanged). This operation is useful if the content of a configuration is to be exported or processed by an external
1344     * component that does not support variable interpolation.
1345     *
1346     * @return a configuration with all variables interpolated
1347     * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if this configuration cannot be cloned
1348     * @since 1.5
1349     */
1350    public Configuration interpolatedConfiguration() {
1351        // first clone this configuration
1352        final AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils.cloneConfiguration(this);
1353
1354        // now perform interpolation
1355        c.setListDelimiterHandler(new DisabledListDelimiterHandler());
1356        for (final Iterator<String> it = getKeys(); it.hasNext();) {
1357            final String key = it.next();
1358            c.setProperty(key, getList(key));
1359        }
1360
1361        c.setListDelimiterHandler(getListDelimiterHandler());
1362        return c;
1363    }
1364
1365    /**
1366     * Initializes the logger. Supports <b>null</b> input. This method can be called by derived classes in order to enable
1367     * logging.
1368     *
1369     * @param log the logger
1370     * @since 2.0
1371     */
1372    protected final void initLogger(final ConfigurationLogger log) {
1373        this.log = log != null ? log : ConfigurationLogger.newDummyLogger();
1374    }
1375
1376    /**
1377     * Encodes a property value so that it can be added to this configuration. This method deals with list delimiters. The
1378     * passed in object has to be escaped so that an add operation yields the same result. If it is a list, all of its
1379     * values have to be escaped.
1380     *
1381     * @param value the value to be encoded
1382     * @return the encoded value
1383     */
1384    private Object encodeForCopy(final Object value) {
1385        if (value instanceof Collection) {
1386            return encodeListForCopy((Collection<?>) value);
1387        }
1388        return getListDelimiterHandler().escape(value, ListDelimiterHandler.NOOP_TRANSFORMER);
1389    }
1390
1391    /**
1392     * Encodes a list with property values so that it can be added to this configuration. This method calls
1393     * {@code encodeForCopy()} for all list elements.
1394     *
1395     * @param values the list to be encoded
1396     * @return a list with encoded elements
1397     */
1398    private Object encodeListForCopy(final Collection<?> values) {
1399        final List<Object> result = new ArrayList<>(values.size());
1400        for (final Object value : values) {
1401            result.add(encodeForCopy(value));
1402        }
1403        return result;
1404    }
1405
1406    /**
1407     * Obtains the property value for the specified key and converts it to the given target class.
1408     *
1409     * @param <T> the target type of the conversion
1410     * @param cls the target class
1411     * @param key the key of the desired property
1412     * @param defaultValue a default value
1413     * @return the converted value of this property
1414     * @throws ConversionException if the conversion cannot be performed
1415     */
1416    private <T> T getAndConvertProperty(final Class<T> cls, final String key, final T defaultValue) {
1417        final Object value = getProperty(key);
1418        try {
1419            return ObjectUtils.defaultIfNull(getConversionHandler().to(value, cls, getInterpolator()), defaultValue);
1420        } catch (final ConversionException cex) {
1421            // improve error message
1422            throw new ConversionException(String.format("Key '%s' cannot be converted to class %s. Value is: '%s'.", key, cls.getName(), String.valueOf(value)),
1423                cex.getCause());
1424        }
1425    }
1426
1427    /**
1428     * Helper method for obtaining a property value with a type conversion.
1429     *
1430     * @param <T> the target type of the conversion
1431     * @param cls the target class
1432     * @param key the key of the desired property
1433     * @param defValue a default value
1434     * @param throwOnMissing a flag whether an exception should be thrown for a missing value
1435     * @return the converted value
1436     */
1437    private <T> T convert(final Class<T> cls, final String key, final T defValue, final boolean throwOnMissing) {
1438        if (cls.isArray()) {
1439            return cls.cast(convertToArray(cls.getComponentType(), key, defValue));
1440        }
1441
1442        final T result = getAndConvertProperty(cls, key, defValue);
1443        if (result == null) {
1444            if (throwOnMissing && isThrowExceptionOnMissing()) {
1445                throwMissingPropertyException(key);
1446            }
1447            return defValue;
1448        }
1449
1450        return result;
1451    }
1452
1453    /**
1454     * Performs a conversion to an array result class. This implementation delegates to the {@link ConversionHandler} to
1455     * perform the actual type conversion. If this results in a <b>null</b> result (because the property is undefined), the
1456     * default value is returned. It is checked whether the default value is an array with the correct component type. If
1457     * not, an exception is thrown.
1458     *
1459     * @param cls the component class of the array
1460     * @param key the configuration key
1461     * @param defaultValue an optional default value
1462     * @return the converted array
1463     * @throws IllegalArgumentException if the default value is not a compatible array
1464     */
1465    private Object convertToArray(final Class<?> cls, final String key, final Object defaultValue) {
1466        checkDefaultValueArray(cls, defaultValue);
1467        return ObjectUtils.defaultIfNull(getConversionHandler().toArray(getProperty(key), cls, getInterpolator()), defaultValue);
1468    }
1469
1470    /**
1471     * Checks an object provided as default value for the {@code getArray()} method. Throws an exception if this is not an
1472     * array with the correct component type.
1473     *
1474     * @param cls the component class for the array
1475     * @param defaultValue the default value object to be checked
1476     * @throws IllegalArgumentException if this is not a valid default object
1477     */
1478    private static void checkDefaultValueArray(final Class<?> cls, final Object defaultValue) {
1479        if (defaultValue != null && (!defaultValue.getClass().isArray() || !cls.isAssignableFrom(defaultValue.getClass().getComponentType()))) {
1480            throw new IllegalArgumentException(
1481                "The type of the default value (" + defaultValue.getClass() + ")" + " is not an array of the specified class (" + cls + ")");
1482        }
1483    }
1484
1485    /**
1486     * Handles the default collection for a collection conversion. This method fills the target collection with the content
1487     * of the default collection. Both collections may be <b>null</b>.
1488     *
1489     * @param target the target collection
1490     * @param defaultValue the default collection
1491     * @return the initialized target collection
1492     */
1493    private static <T> Collection<T> handleDefaultCollection(final Collection<T> target, final Collection<T> defaultValue) {
1494        if (defaultValue == null) {
1495            return null;
1496        }
1497
1498        final Collection<T> result;
1499        if (target == null) {
1500            result = new ArrayList<>(defaultValue);
1501        } else {
1502            target.addAll(defaultValue);
1503            result = target;
1504        }
1505        return result;
1506    }
1507
1508    /**
1509     * Checks whether the specified value is <b>null</b> and throws an exception in this case. This method is used by
1510     * conversion methods returning primitive Java types. Here values to be returned must not be <b>null</b>.
1511     *
1512     * @param <T> the type of the object to be checked
1513     * @param key the key which caused the problem
1514     * @param value the value to be checked
1515     * @return the passed in value for chaining this method call
1516     * @throws NoSuchElementException if the value is <b>null</b>
1517     */
1518    private static <T> T checkNonNullValue(final String key, final T value) {
1519        if (value == null) {
1520            throwMissingPropertyException(key);
1521        }
1522        return value;
1523    }
1524
1525    /**
1526     * Helper method for throwing an exception for a key that does not map to an existing object.
1527     *
1528     * @param key the key (to be part of the error message)
1529     */
1530    private static void throwMissingPropertyException(final String key) {
1531        throw new NoSuchElementException(String.format("Key '%s' does not map to an existing object!", key));
1532    }
1533}