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