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