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