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