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