    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}