AbstractConfiguration.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.configuration2;

  18. import java.math.BigDecimal;
  19. import java.math.BigInteger;
  20. import java.time.Duration;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.Collection;
  24. import java.util.Collections;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.NoSuchElementException;
  29. import java.util.Objects;
  30. import java.util.Properties;
  31. import java.util.concurrent.atomic.AtomicReference;
  32. import java.util.stream.Collectors;

  33. import org.apache.commons.configuration2.convert.ConversionHandler;
  34. import org.apache.commons.configuration2.convert.DefaultConversionHandler;
  35. import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
  36. import org.apache.commons.configuration2.convert.ListDelimiterHandler;
  37. import org.apache.commons.configuration2.event.BaseEventSource;
  38. import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
  39. import org.apache.commons.configuration2.event.ConfigurationEvent;
  40. import org.apache.commons.configuration2.event.EventListener;
  41. import org.apache.commons.configuration2.ex.ConversionException;
  42. import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
  43. import org.apache.commons.configuration2.interpol.InterpolatorSpecification;
  44. import org.apache.commons.configuration2.interpol.Lookup;
  45. import org.apache.commons.configuration2.io.ConfigurationLogger;
  46. import org.apache.commons.configuration2.sync.LockMode;
  47. import org.apache.commons.configuration2.sync.NoOpSynchronizer;
  48. import org.apache.commons.configuration2.sync.Synchronizer;
  49. import org.apache.commons.lang3.ArrayUtils;
  50. import org.apache.commons.lang3.ClassUtils;
  51. import org.apache.commons.lang3.ObjectUtils;
  52. import org.apache.commons.lang3.StringUtils;
  53. import org.apache.commons.lang3.function.FailableRunnable;
  54. import org.apache.commons.lang3.function.FailableSupplier;

  55. /**
  56.  * <p>
  57.  * Abstract configuration class. Provides basic functionality but does not store any data.
  58.  * </p>
  59.  * <p>
  60.  * If you want to write your own Configuration class then you should implement only abstract methods from this class. A
  61.  * lot of functionality needed by typical implementations of the {@code Configuration} interface is already provided by
  62.  * this base class. Following is a list of features implemented here:
  63.  * </p>
  64.  * <ul>
  65.  * <li>Data conversion support. The various data types required by the {@code Configuration} interface are already
  66.  * handled by this base class. A concrete sub class only needs to provide a generic {@code getProperty()} method.</li>
  67.  * <li>Support for variable interpolation. Property values containing special variable tokens (like {@code ${var}}) will
  68.  * be replaced by their corresponding values.</li>
  69.  * <li>Optional support for string lists. The values of properties to be added to this configuration are checked whether
  70.  * they contain a list delimiter character. If this is the case and if list splitting is enabled, the string is split
  71.  * and multiple values are added for this property. List splitting is controlled by a {@link ListDelimiterHandler}
  72.  * object which can be set using the {@link #setListDelimiterHandler(ListDelimiterHandler)} method. It is disabled per
  73.  * default. To enable this feature, set a suitable {@code ListDelimiterHandler}, for example an instance of
  74.  * {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler DefaultListDelimiterHandler} configured
  75.  * with the desired list delimiter character.</li>
  76.  * <li>Allows specifying how missing properties are treated. Per default the get methods returning an object will return
  77.  * <strong>null</strong> if the searched property key is not found (and no default value is provided). With the
  78.  * {@code setThrowExceptionOnMissing()} method this behavior can be changed to throw an exception when a requested
  79.  * property cannot be found.</li>
  80.  * <li>Basic event support. Whenever this configuration is modified registered event listeners are notified. Refer to
  81.  * the various {@code EVENT_XXX} constants to get an impression about which event types are supported.</li>
  82.  * <li>Support for proper synchronization based on the {@link Synchronizer} interface.</li>
  83.  * </ul>
  84.  * <p>
  85.  * Most methods defined by the {@code Configuration} interface are already implemented in this class. Many method
  86.  * implementations perform basic book-keeping tasks (for example firing events, handling synchronization), and then delegate to
  87.  * other (protected) methods executing the actual work. Subclasses override these protected methods to define or adapt
  88.  * behavior. The public entry point methods are final to prevent subclasses from breaking basic functionality.
  89.  * </p>
  90.  */
  91. public abstract class AbstractConfiguration extends BaseEventSource implements Configuration {

  92.     /**
  93.      * Default configuration delimiter for properties and keys.
  94.      */
  95.     static final String DELIMITER = ".";

  96.     /**
  97.      * Checks an object provided as default value for the {@code getArray()} method. Throws an exception if this is not an
  98.      * array with the correct component type.
  99.      *
  100.      * @param cls the component class for the array
  101.      * @param defaultValue the default value object to be checked
  102.      * @throws IllegalArgumentException if this is not a valid default object
  103.      */
  104.     private static void checkDefaultValueArray(final Class<?> cls, final Object defaultValue) {
  105.         if (defaultValue != null && (!defaultValue.getClass().isArray() || !cls.isAssignableFrom(defaultValue.getClass().getComponentType()))) {
  106.             throw new IllegalArgumentException(
  107.                 "The type of the default value (" + defaultValue.getClass() + ")" + " is not an array of the specified class (" + cls + ")");
  108.         }
  109.     }

  110.     /**
  111.      * Checks whether the specified value is <strong>null</strong> 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 <strong>null</strong>.
  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 <strong>null</strong>
  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.      * Finds a {@code ConfigurationLookup} pointing to the specified configuration in the default lookups for the specified
  128.      * {@code ConfigurationInterpolator}.
  129.      *
  130.      * @param ci the {@code ConfigurationInterpolator} in question
  131.      * @param targetConf the target configuration of the searched lookup
  132.      * @return the found {@code Lookup} object or <strong>null</strong>
  133.      */
  134.     private static Lookup findConfigurationLookup(final ConfigurationInterpolator ci, final ImmutableConfiguration targetConf) {
  135.         for (final Lookup l : ci.getDefaultLookups()) {
  136.             if (l instanceof ConfigurationLookup && targetConf == ((ConfigurationLookup) l).getConfiguration()) {
  137.                 return l;
  138.             }
  139.         }
  140.         return null;
  141.     }

  142.     /**
  143.      * Handles the default collection for a collection conversion. This method fills the target collection with the content
  144.      * of the default collection. Both collections may be <strong>null</strong>.
  145.      *
  146.      * @param target the target collection
  147.      * @param defaultValue the default collection
  148.      * @return the initialized target collection
  149.      */
  150.     private static <T> Collection<T> handleDefaultCollection(final Collection<T> target, final Collection<T> defaultValue) {
  151.         if (defaultValue == null) {
  152.             return null;
  153.         }

  154.         final Collection<T> result;
  155.         if (target == null) {
  156.             result = new ArrayList<>(defaultValue);
  157.         } else {
  158.             target.addAll(defaultValue);
  159.             result = target;
  160.         }
  161.         return result;
  162.     }

  163.     /**
  164.      * Helper method for throwing an exception for a key that does not map to an existing object.
  165.      *
  166.      * @param key the key (to be part of the error message)
  167.      */
  168.     private static void throwMissingPropertyException(final String key) {
  169.         throw new NoSuchElementException(String.format("Key '%s' does not map to an existing object!", key));
  170.     }

  171.     /** The list delimiter handler. */
  172.     private ListDelimiterHandler listDelimiterHandler;

  173.     /** The conversion handler. */
  174.     private ConversionHandler conversionHandler;

  175.     /**
  176.      * Whether the configuration should throw NoSuchElementExceptions or simply return null when a property does not exist.
  177.      * Defaults to return null.
  178.      */
  179.     private boolean throwExceptionOnMissing;

  180.     /** Stores a reference to the object that handles variable interpolation. */
  181.     private AtomicReference<ConfigurationInterpolator> interpolator;

  182.     /** The object responsible for synchronization. */
  183.     private volatile Synchronizer synchronizer = NoOpSynchronizer.INSTANCE;

  184.     /** The object used for dealing with encoded property values. */
  185.     private ConfigurationDecoder configurationDecoder;

  186.     /** Stores the logger. */
  187.     private ConfigurationLogger log;

  188.     /**
  189.      * Creates a new instance of {@code AbstractConfiguration}.
  190.      */
  191.     public AbstractConfiguration() {
  192.         interpolator = new AtomicReference<>();
  193.         initLogger(null);
  194.         installDefaultInterpolator();
  195.         listDelimiterHandler = DisabledListDelimiterHandler.INSTANCE;
  196.         conversionHandler = DefaultConversionHandler.INSTANCE;
  197.     }

  198.     /**
  199.      * Adds a special {@link EventListener} object to this configuration that will log all internal errors. This method is
  200.      * intended to be used by certain derived classes, for which it is known that they can fail on property access (for example
  201.      * {@code DatabaseConfiguration}).
  202.      *
  203.      * @since 1.4
  204.      */
  205.     public final void addErrorLogListener() {
  206.         addEventListener(ConfigurationErrorEvent.ANY, event -> getLogger().warn("Internal error", event.getCause()));
  207.     }

  208.     @Override
  209.     public final void addProperty(final String key, final Object value) {
  210.         syncWrite(() -> {
  211.             fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, true);
  212.             addPropertyInternal(key, value);
  213.             fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, false);
  214.         }, false);
  215.     }

  216.     /**
  217.      * Adds a key/value pair to the Configuration. Override this method to provide write access to underlying Configuration
  218.      * store.
  219.      *
  220.      * @param key key to use for mapping
  221.      * @param value object to store
  222.      */
  223.     protected abstract void addPropertyDirect(String key, Object value);

  224.     /**
  225.      * Actually adds a property to this configuration. This method is called by {@code addProperty()}. It performs list
  226.      * splitting if necessary and delegates to {@link #addPropertyDirect(String, Object)} for every single property value.
  227.      *
  228.      * @param key the key of the property to be added
  229.      * @param value the new property value
  230.      * @since 2.0
  231.      */
  232.     protected void addPropertyInternal(final String key, final Object value) {
  233.         getListDelimiterHandler().parse(value).forEach(obj -> addPropertyDirect(key, obj));
  234.     }

  235.     /**
  236.      * Appends the content of the specified configuration to this configuration. The values of all properties contained in
  237.      * the specified configuration will be appended to this configuration. So if a property is already present in this
  238.      * configuration, its new value will be a union of the values in both configurations. <em>Note:</em> This method won't
  239.      * work well when appending hierarchical configurations because it is not able to copy information about the properties'
  240.      * structure (i.e. the parent-child-relationships will get lost). So when dealing with hierarchical configuration
  241.      * objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be used.
  242.      *
  243.      * @param configuration the configuration to be appended (can be <strong>null</strong>, then this operation will have no effect)
  244.      * @since 1.5
  245.      */
  246.     public void append(final Configuration configuration) {
  247.         if (configuration != null) {
  248.             configuration.lock(LockMode.READ);
  249.             try {
  250.                 configuration.getKeys().forEachRemaining(key -> addProperty(key, encodeForCopy(configuration.getProperty(key))));
  251.             } finally {
  252.                 configuration.unlock(LockMode.READ);
  253.             }
  254.         }
  255.     }

  256.     /**
  257.      * Notifies this configuration's {@link Synchronizer} that a read operation is about to start. This method is called by
  258.      * all methods which access this configuration in a read-only mode. Subclasses may override it to perform additional
  259.      * actions before this read operation. The boolean <em>optimize</em> argument can be evaluated by overridden methods in
  260.      * derived classes. Some operations which require a lock do not need a fully initialized configuration object. By
  261.      * setting this flag to <strong>true</strong>, such operations can give a corresponding hint. An overridden
  262.      * implementation of {@code beginRead()} can then decide to skip some initialization steps. All basic operations in this
  263.      * class (and most of the basic {@code Configuration} implementations) call this method with a parameter value of
  264.      * <strong>false</strong>. <strong>In any case the inherited method must be called! Otherwise, proper synchronization is
  265.      * not guaranteed.</strong>
  266.      *
  267.      * @param optimize a flag whether optimization can be performed
  268.      * @since 2.0
  269.      */
  270.     protected void beginRead(final boolean optimize) {
  271.         getSynchronizer().beginRead();
  272.     }

  273.     /**
  274.      * Notifies this configuration's {@link Synchronizer} that an update operation is about to start. This method is called
  275.      * by all methods which modify this configuration. Subclasses may override it to perform additional operations before an
  276.      * update. For a description of the boolean <em>optimize</em> argument refer to the documentation of
  277.      * {@code beginRead()}. <strong>In any case the inherited method must be called! Otherwise, proper synchronization is
  278.      * not guaranteed.</strong>
  279.      *
  280.      * @param optimize a flag whether optimization can be performed
  281.      * @see #beginRead(boolean)
  282.      * @since 2.0
  283.      */
  284.     protected void beginWrite(final boolean optimize) {
  285.         getSynchronizer().beginWrite();
  286.     }

  287.     @Override
  288.     public final void clear() {
  289.         syncWrite(() -> {
  290.             fireEvent(ConfigurationEvent.CLEAR, null, null, true);
  291.             clearInternal();
  292.             fireEvent(ConfigurationEvent.CLEAR, null, null, false);
  293.         }, false);
  294.     }

  295.     /**
  296.      * Clears the whole configuration. This method is called by {@code clear()} after some preparations have been made. This
  297.      * base implementation uses the iterator provided by {@code getKeys()} to remove every single property. Subclasses
  298.      * should override this method if there is a more efficient way of clearing the configuration.
  299.      */
  300.     protected void clearInternal() {
  301.         setDetailEvents(false);
  302.         boolean useIterator = true;
  303.         try {
  304.             final Iterator<String> it = getKeys();
  305.             while (it.hasNext()) {
  306.                 final String key = it.next();
  307.                 if (useIterator) {
  308.                     try {
  309.                         it.remove();
  310.                     } catch (final UnsupportedOperationException usoex) {
  311.                         useIterator = false;
  312.                     }
  313.                 }

  314.                 if (useIterator && containsKey(key)) {
  315.                     useIterator = false;
  316.                 }

  317.                 if (!useIterator) {
  318.                     // workaround for Iterators that do not remove the
  319.                     // property
  320.                     // on calling remove() or do not support remove() at all
  321.                     clearProperty(key);
  322.                 }
  323.             }
  324.         } finally {
  325.             setDetailEvents(true);
  326.         }
  327.     }

  328.     /**
  329.      * Removes the specified property from this configuration. This implementation performs some preparations and then
  330.      * delegates to {@code clearPropertyDirect()}, which will do the real work.
  331.      *
  332.      * @param key the key to be removed
  333.      */
  334.     @Override
  335.     public final void clearProperty(final String key) {
  336.         syncWrite(() -> {
  337.             fireEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, true);
  338.             clearPropertyDirect(key);
  339.             fireEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, false);
  340.         }, false);
  341.     }

  342.     /**
  343.      * Removes the specified property from this configuration. This method is called by {@code clearProperty()} after it has
  344.      * done some preparations. It must be overridden in sub classes.
  345.      *
  346.      * @param key the key to be removed
  347.      */
  348.     protected abstract void clearPropertyDirect(String key);

  349.     /**
  350.      * Creates a clone of the {@code ConfigurationInterpolator} used by this instance. This method can be called by
  351.      * {@code clone()} implementations of derived classes. Normally, the {@code ConfigurationInterpolator} of a
  352.      * configuration instance must not be shared with other instances because it contains a specific {@code Lookup} object
  353.      * pointing to the owning configuration. This has to be taken into account when cloning a configuration. This method
  354.      * creates a new {@code ConfigurationInterpolator} for this configuration instance which contains all lookup objects
  355.      * from the original {@code ConfigurationInterpolator} except for the configuration specific lookup pointing to the
  356.      * passed in original configuration. This one is replaced by a corresponding {@code Lookup} referring to this
  357.      * configuration.
  358.      *
  359.      * @param orgConfig the original configuration from which this one was cloned
  360.      * @since 2.0
  361.      */
  362.     protected void cloneInterpolator(final AbstractConfiguration orgConfig) {
  363.         interpolator = new AtomicReference<>();
  364.         final ConfigurationInterpolator orgInterpolator = orgConfig.getInterpolator();
  365.         final List<Lookup> defaultLookups = orgInterpolator.getDefaultLookups();
  366.         final Lookup lookup = findConfigurationLookup(orgInterpolator, orgConfig);
  367.         if (lookup != null) {
  368.             defaultLookups.remove(lookup);
  369.         }

  370.         installInterpolator(orgInterpolator.getLookups(), defaultLookups);
  371.     }

  372.     /**
  373.      * Checks if the specified value exists in the properties structure mapped by the provided keys.
  374.      *
  375.      * @param keys an Iterator of String keys to search for the value
  376.      * @param value the String value to search for in the properties
  377.      * @return true if the value is found in the properties, false otherwise
  378.      * @since 2.11.0
  379.      */
  380.     protected boolean contains(final Iterator<String> keys, final Object value) {
  381.         while (keys.hasNext()) {
  382.             if (Objects.equals(value, getProperty(keys.next()))) {
  383.                 return true;
  384.             }
  385.         }
  386.         return false;
  387.     }

  388.     /**
  389.      * {@inheritDoc} This implementation handles synchronization and delegates to {@code containsKeyInternal()}.
  390.      */
  391.     @Override
  392.     public final boolean containsKey(final String key) {
  393.         return syncRead(() -> containsKeyInternal(key), false);
  394.     }

  395.     /**
  396.      * Actually checks whether the specified key is contained in this configuration. This method is called by
  397.      * {@code containsKey()}. It has to be defined by concrete subclasses.
  398.      *
  399.      * @param key the key in question
  400.      * @return <strong>true</strong> if this key is contained in this configuration, <strong>false</strong> otherwise
  401.      * @since 2.0
  402.      */
  403.     protected abstract boolean containsKeyInternal(String key);

  404.     /**
  405.      * {@inheritDoc} This implementation handles synchronization and delegates to {@code containsKeyInternal()}.
  406.      * @since 2.11.0
  407.      */
  408.     @Override
  409.     public final boolean containsValue(final Object value) {
  410.         return syncRead(() -> containsValueInternal(value), false);
  411.     }

  412.     /**
  413.      * 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
  414.      * {@link #containsKeyInternal containsKey} method.
  415.      * <p>
  416.      * The implementation of this method will be different depending on the type of Configuration used.
  417.      * </p>
  418.      *
  419.      * <p>
  420.      * Note that this method is identical in functionality to {@link #containsValue containsValue}, (which is part of the {@link ImmutableConfiguration}
  421.      * interface).
  422.      * </p>
  423.      *
  424.      * @param value the value in question
  425.      * @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;
  426.      *         {@code false} otherwise.
  427.      * @since 2.11.0
  428.      */
  429.     protected abstract boolean containsValueInternal(Object value);

  430.     /**
  431.      * Helper method for obtaining a property value with a type conversion.
  432.      *
  433.      * @param <T> the target type of the conversion
  434.      * @param cls the target class
  435.      * @param key the key of the desired property
  436.      * @param defValue a default value
  437.      * @param throwOnMissing a flag whether an exception should be thrown for a missing value
  438.      * @return the converted value
  439.      */
  440.     private <T> T convert(final Class<T> cls, final String key, final T defValue, final boolean throwOnMissing) {
  441.         if (cls.isArray()) {
  442.             return cls.cast(convertToArray(cls.getComponentType(), key, defValue));
  443.         }

  444.         final T result = getAndConvertProperty(cls, key, defValue);
  445.         if (result == null) {
  446.             if (throwOnMissing && isThrowExceptionOnMissing()) {
  447.                 throwMissingPropertyException(key);
  448.             }
  449.             return defValue;
  450.         }

  451.         return result;
  452.     }

  453.     /**
  454.      * Performs a conversion to an array result class. This implementation delegates to the {@link ConversionHandler} to
  455.      * perform the actual type conversion. If this results in a <strong>null</strong> result (because the property is undefined), the
  456.      * default value is returned. It is checked whether the default value is an array with the correct component type. If
  457.      * not, an exception is thrown.
  458.      *
  459.      * @param cls the component class of the array
  460.      * @param key the configuration key
  461.      * @param defaultValue an optional default value
  462.      * @return the converted array
  463.      * @throws IllegalArgumentException if the default value is not a compatible array
  464.      */
  465.     private Object convertToArray(final Class<?> cls, final String key, final Object defaultValue) {
  466.         checkDefaultValueArray(cls, defaultValue);
  467.         return ObjectUtils.defaultIfNull(getConversionHandler().toArray(getProperty(key), cls, getInterpolator()), defaultValue);
  468.     }

  469.     /**
  470.      * Copies the content of the specified configuration into this configuration. If the specified configuration contains a
  471.      * key that is also present in this configuration, the value of this key will be replaced by the new value.
  472.      * <em>Note:</em> This method won't work well when copying hierarchical configurations because it is not able to copy
  473.      * information about the properties' structure (i.e. the parent-child-relationships will get lost). So when dealing with
  474.      * hierarchical configuration objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be
  475.      * used.
  476.      *
  477.      * @param configuration the configuration to copy (can be <strong>null</strong>, then this operation will have no effect)
  478.      * @since 1.5
  479.      */
  480.     public void copy(final Configuration configuration) {
  481.         if (configuration != null) {
  482.             configuration.lock(LockMode.READ);
  483.             try {
  484.                 configuration.getKeys().forEachRemaining(key -> setProperty(key, encodeForCopy(configuration.getProperty(key))));
  485.             } finally {
  486.                 configuration.unlock(LockMode.READ);
  487.             }
  488.         }
  489.     }

  490.     /**
  491.      * Encodes a property value so that it can be added to this configuration. This method deals with list delimiters. The
  492.      * passed in object has to be escaped so that an add operation yields the same result. If it is a list, all of its
  493.      * values have to be escaped.
  494.      *
  495.      * @param value the value to be encoded
  496.      * @return the encoded value
  497.      */
  498.     private Object encodeForCopy(final Object value) {
  499.         if (value instanceof Collection) {
  500.             return encodeListForCopy((Collection<?>) value);
  501.         }
  502.         return getListDelimiterHandler().escape(value, ListDelimiterHandler.NOOP_TRANSFORMER);
  503.     }

  504.     /**
  505.      * Encodes a list with property values so that it can be added to this configuration. This method calls
  506.      * {@code encodeForCopy()} for all list elements.
  507.      *
  508.      * @param values the list to be encoded
  509.      * @return a list with encoded elements
  510.      */
  511.     private Object encodeListForCopy(final Collection<?> values) {
  512.         return values.stream().map(this::encodeForCopy).collect(Collectors.toList());
  513.     }

  514.     /**
  515.      * Notifies this configuration's {@link Synchronizer} that a read operation has finished. This method is called by all
  516.      * methods which access this configuration in a read-only manner at the end of their execution. Subclasses may override
  517.      * it to perform additional actions after this read operation. <strong>In any case the inherited method must be called!
  518.      * Otherwise, the read lock will not be released.</strong>
  519.      *
  520.      * @since 2.0
  521.      */
  522.     protected void endRead() {
  523.         getSynchronizer().endRead();
  524.     }

  525.     /**
  526.      * Notifies this configuration's {@link Synchronizer} that an update operation has finished. This method is called by
  527.      * all methods which modify this configuration at the end of their execution. Subclasses may override it to perform
  528.      * additional operations after an update. <strong>In any case the inherited method must be called! Otherwise, the write
  529.      * lock will not be released.</strong>
  530.      *
  531.      * @since 2.0
  532.      */
  533.     protected void endWrite() {
  534.         getSynchronizer().endWrite();
  535.     }

  536.     /**
  537.      * Finds a {@code ConfigurationLookup} pointing to this configuration in the default lookups of the specified
  538.      * {@code ConfigurationInterpolator}. This method is called to ensure that there is exactly one default lookup querying
  539.      * this configuration.
  540.      *
  541.      * @param ci the {@code ConfigurationInterpolator} in question
  542.      * @return the found {@code Lookup} object or <strong>null</strong>
  543.      */
  544.     private Lookup findConfigurationLookup(final ConfigurationInterpolator ci) {
  545.         return findConfigurationLookup(ci, this);
  546.     }

  547.     @Override
  548.     public <T> T get(final Class<T> cls, final String key) {
  549.         return convert(cls, key, null, true);
  550.     }

  551.     /**
  552.      * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion.
  553.      */
  554.     @Override
  555.     public <T> T get(final Class<T> cls, final String key, final T defaultValue) {
  556.         return convert(cls, key, defaultValue, false);
  557.     }

  558.     /**
  559.      * Obtains the property value for the specified key and converts it to the given target class.
  560.      *
  561.      * @param <T> the target type of the conversion
  562.      * @param cls the target class
  563.      * @param key the key of the desired property
  564.      * @param defaultValue a default value
  565.      * @return the converted value of this property
  566.      * @throws ConversionException if the conversion cannot be performed
  567.      */
  568.     private <T> T getAndConvertProperty(final Class<T> cls, final String key, final T defaultValue) {
  569.         final Object value = getProperty(key);
  570.         try {
  571.             return ObjectUtils.defaultIfNull(getConversionHandler().to(value, cls, getInterpolator()), defaultValue);
  572.         } catch (final ConversionException cex) {
  573.             // improve error message
  574.             throw new ConversionException(String.format("Key '%s' cannot be converted to class %s. Value is: '%s'.", key, cls.getName(), String.valueOf(value)),
  575.                 cex.getCause());
  576.         }
  577.     }

  578.     @Override
  579.     public Object getArray(final Class<?> cls, final String key) {
  580.         return getArray(cls, key, null);
  581.     }

  582.     /**
  583.      * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion.
  584.      * If this results in a <strong>null</strong> result (because the property is undefined), the default value is returned. It is
  585.      * checked whether the default value is an array with the correct component type. If not, an exception is thrown.
  586.      *
  587.      * @throws IllegalArgumentException if the default value is not a compatible array
  588.      */
  589.     @Override
  590.     public Object getArray(final Class<?> cls, final String key, final Object defaultValue) {
  591.         return convertToArray(cls, key, defaultValue);
  592.     }

  593.     /**
  594.      * {@inheritDoc}
  595.      *
  596.      * @see #setThrowExceptionOnMissing(boolean)
  597.      */
  598.     @Override
  599.     public BigDecimal getBigDecimal(final String key) {
  600.         return convert(BigDecimal.class, key, null, true);
  601.     }

  602.     @Override
  603.     public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) {
  604.         return convert(BigDecimal.class, key, defaultValue, false);
  605.     }

  606.     /**
  607.      * {@inheritDoc}
  608.      *
  609.      * @see #setThrowExceptionOnMissing(boolean)
  610.      */
  611.     @Override
  612.     public BigInteger getBigInteger(final String key) {
  613.         return convert(BigInteger.class, key, null, true);
  614.     }

  615.     @Override
  616.     public BigInteger getBigInteger(final String key, final BigInteger defaultValue) {
  617.         return convert(BigInteger.class, key, defaultValue, false);
  618.     }

  619.     @Override
  620.     public boolean getBoolean(final String key) {
  621.         final Boolean b = convert(Boolean.class, key, null, true);
  622.         return checkNonNullValue(key, b).booleanValue();
  623.     }

  624.     @Override
  625.     public boolean getBoolean(final String key, final boolean defaultValue) {
  626.         return getBoolean(key, Boolean.valueOf(defaultValue)).booleanValue();
  627.     }

  628.     /**
  629.      * Obtains the value of the specified key and tries to convert it into a {@code Boolean} object. If the property has no
  630.      * value, the passed in default value will be used.
  631.      *
  632.      * @param key the key of the property
  633.      * @param defaultValue the default value
  634.      * @return the value of this key converted to a {@code Boolean}
  635.      * @throws ConversionException if the value cannot be converted to a {@code Boolean}
  636.      */
  637.     @Override
  638.     public Boolean getBoolean(final String key, final Boolean defaultValue) {
  639.         return convert(Boolean.class, key, defaultValue, false);
  640.     }

  641.     @Override
  642.     public byte getByte(final String key) {
  643.         final Byte b = convert(Byte.class, key, null, true);
  644.         return checkNonNullValue(key, b).byteValue();
  645.     }

  646.     @Override
  647.     public byte getByte(final String key, final byte defaultValue) {
  648.         return getByte(key, Byte.valueOf(defaultValue)).byteValue();
  649.     }

  650.     @Override
  651.     public Byte getByte(final String key, final Byte defaultValue) {
  652.         return convert(Byte.class, key, defaultValue, false);
  653.     }

  654.     @Override
  655.     public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target) {
  656.         return getCollection(cls, key, target, null);
  657.     }

  658.     /**
  659.      * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual conversion. If no
  660.      * target collection is provided, an {@code ArrayList} is created.
  661.      */
  662.     @Override
  663.     public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target, final Collection<T> defaultValue) {
  664.         final Object src = getProperty(key);
  665.         if (src == null) {
  666.             return handleDefaultCollection(target, defaultValue);
  667.         }

  668.         final Collection<T> targetCol = target != null ? target : new ArrayList<>();
  669.         getConversionHandler().toCollection(src, cls, getInterpolator(), targetCol);
  670.         return targetCol;
  671.     }

  672.     /**
  673.      * Gets the {@code ConfigurationDecoder} used by this instance.
  674.      *
  675.      * @return the {@code ConfigurationDecoder}
  676.      * @since 2.0
  677.      */
  678.     public ConfigurationDecoder getConfigurationDecoder() {
  679.         return configurationDecoder;
  680.     }

  681.     /**
  682.      * Gets the {@code ConversionHandler} used by this instance.
  683.      *
  684.      * @return the {@code ConversionHandler}
  685.      * @since 2.0
  686.      */
  687.     public ConversionHandler getConversionHandler() {
  688.         return conversionHandler;
  689.     }

  690.     @Override
  691.     public double getDouble(final String key) {
  692.         final Double d = convert(Double.class, key, null, true);
  693.         return checkNonNullValue(key, d).doubleValue();
  694.     }

  695.     @Override
  696.     public double getDouble(final String key, final double defaultValue) {
  697.         return getDouble(key, Double.valueOf(defaultValue)).doubleValue();
  698.     }

  699.     @Override
  700.     public Double getDouble(final String key, final Double defaultValue) {
  701.         return convert(Double.class, key, defaultValue, false);
  702.     }

  703.     @Override
  704.     public Duration getDuration(final String key) {
  705.         return checkNonNullValue(key, convert(Duration.class, key, null, true));
  706.     }

  707.     @Override
  708.     public Duration getDuration(final String key, final Duration defaultValue) {
  709.         return convert(Duration.class, key, defaultValue, false);
  710.     }

  711.     /**
  712.      * {@inheritDoc} This implementation makes use of the {@code ConfigurationDecoder} set for this configuration. If no
  713.      * such object has been set, an {@code IllegalStateException} exception is thrown.
  714.      *
  715.      * @throws IllegalStateException if no {@code ConfigurationDecoder} is set
  716.      * @see #setConfigurationDecoder(ConfigurationDecoder)
  717.      */
  718.     @Override
  719.     public String getEncodedString(final String key) {
  720.         final ConfigurationDecoder decoder = getConfigurationDecoder();
  721.         if (decoder == null) {
  722.             throw new IllegalStateException("No default ConfigurationDecoder defined!");
  723.         }
  724.         return getEncodedString(key, decoder);
  725.     }

  726.     /**
  727.      * {@inheritDoc} This implementation delegates to {@link #getString(String)} in order to obtain the value of the passed
  728.      * in key. This value is passed to the decoder. Because {@code getString()} is used behind the scenes all standard
  729.      * features like handling of missing keys and interpolation work as expected.
  730.      */
  731.     @Override
  732.     public String getEncodedString(final String key, final ConfigurationDecoder decoder) {
  733.         if (decoder == null) {
  734.             throw new IllegalArgumentException("ConfigurationDecoder must not be null!");
  735.         }

  736.         final String value = getString(key);
  737.         return value != null ? decoder.decode(value) : null;
  738.     }

  739.     @Override
  740.     public float getFloat(final String key) {
  741.         final Float f = convert(Float.class, key, null, true);
  742.         return checkNonNullValue(key, f).floatValue();
  743.     }

  744.     @Override
  745.     public float getFloat(final String key, final float defaultValue) {
  746.         return getFloat(key, Float.valueOf(defaultValue)).floatValue();
  747.     }

  748.     @Override
  749.     public Float getFloat(final String key, final Float defaultValue) {
  750.         return convert(Float.class, key, defaultValue, false);
  751.     }

  752.     @Override
  753.     public int getInt(final String key) {
  754.         final Integer i = convert(Integer.class, key, null, true);
  755.         return checkNonNullValue(key, i).intValue();
  756.     }

  757.     @Override
  758.     public int getInt(final String key, final int defaultValue) {
  759.         return getInteger(key, Integer.valueOf(defaultValue)).intValue();
  760.     }

  761.     @Override
  762.     public Integer getInteger(final String key, final Integer defaultValue) {
  763.         return convert(Integer.class, key, defaultValue, false);
  764.     }

  765.     /**
  766.      * Gets the {@code ConfigurationInterpolator} object that manages the lookup objects for resolving variables.
  767.      * Unless a custom interpolator has been set or the instance has been modified, the returned interpolator will
  768.      * resolve values from this configuration instance and support the
  769.      * {@link ConfigurationInterpolator#getDefaultPrefixLookups() default prefix lookups}.
  770.      *
  771.      * @return the {@code ConfigurationInterpolator} associated with this configuration
  772.      * @since 1.4
  773.      * @see ConfigurationInterpolator#getDefaultPrefixLookups()
  774.      */
  775.     @Override
  776.     public ConfigurationInterpolator getInterpolator() {
  777.         return interpolator.get();
  778.     }

  779.     /**
  780.      * {@inheritDoc} This implementation takes care of synchronization and then delegates to {@code getKeysInternal()} for
  781.      * obtaining the actual iterator. Note that depending on a concrete implementation, an iteration may fail if the
  782.      * configuration is updated concurrently.
  783.      */
  784.     @Override
  785.     public final Iterator<String> getKeys() {
  786.         return syncRead(() -> getKeysInternal(), false);
  787.     }

  788.     /**
  789.      * {@inheritDoc} This implementation returns keys that either match the prefix or start with the prefix followed by a
  790.      * dot ('.'). So the call {@code getKeys("db");} will find the keys {@code db}, {@code db.user}, or {@code db.password},
  791.      * but not the key {@code dbdriver}.
  792.      */
  793.     @Override
  794.     public final Iterator<String> getKeys(final String prefix) {
  795.         return syncRead(() -> getKeysInternal(prefix), false);
  796.     }

  797.     /**
  798.      * {@inheritDoc} This implementation returns keys that either match the prefix or start with the prefix followed by the delimiter.
  799.      * So the call {@code getKeys("db");} will find the keys {@code db}, {@code db@user}, or {@code db@password},
  800.      * but not the key {@code dbdriver}.
  801.      */
  802.     @Override
  803.     public final Iterator<String> getKeys(final String prefix, final String delimiter) {
  804.         return syncRead(() -> getKeysInternal(prefix, delimiter), false);
  805.     }

  806.     /**
  807.      * Actually creates an iterator for iterating over the keys in this configuration. This method is called by
  808.      * {@code getKeys()}, it has to be defined by concrete subclasses.
  809.      *
  810.      * @return an {@code Iterator} with all property keys in this configuration
  811.      * @since 2.0
  812.      */
  813.     protected abstract Iterator<String> getKeysInternal();

  814.     /**
  815.      * Gets an {@code Iterator} with all property keys starting with the specified prefix. This method is called by
  816.      * {@link #getKeys(String)}. It is fully implemented by delegating to {@code getKeysInternal()} and returning a special
  817.      * iterator which filters for the passed in prefix. Subclasses can override it if they can provide a more efficient way
  818.      * to iterate over specific keys only.
  819.      *
  820.      * @param prefix the prefix for the keys to be taken into account
  821.      * @return an {@code Iterator} returning the filtered keys
  822.      * @since 2.0
  823.      */
  824.     protected Iterator<String> getKeysInternal(final String prefix) {
  825.         return new PrefixedKeysIterator(getKeysInternal(), prefix);
  826.     }

  827.     /**
  828.      * Gets an {@code Iterator} with all property keys starting with the specified prefix and specified delimiter. This method is called by
  829.      * {@link #getKeys(String)}. It is fully implemented by delegating to {@code getKeysInternal()} and returning a special
  830.      * iterator which filters for the passed in prefix. Subclasses can override it if they can provide a more efficient way
  831.      * to iterate over specific keys only.
  832.      *
  833.      * @param prefix the prefix for the keys to be taken into account
  834.      * @param delimiter the prefix delimiter
  835.      * @return an {@code Iterator} returning the filtered keys
  836.      * @since 2.10.0
  837.      */
  838.     protected Iterator<String> getKeysInternal(final String prefix, final String delimiter) {
  839.         return new PrefixedKeysIterator(getKeysInternal(), prefix, delimiter);
  840.     }

  841.     @Override
  842.     public <T> List<T> getList(final Class<T> cls, final String key) {
  843.         return getList(cls, key, null);
  844.     }

  845.     /**
  846.      * {@inheritDoc} This implementation delegates to the generic {@code getCollection()}. As target collection a newly
  847.      * created {@code ArrayList} is passed in.
  848.      */
  849.     @Override
  850.     public <T> List<T> getList(final Class<T> cls, final String key, final List<T> defaultValue) {
  851.         final List<T> result = new ArrayList<>();
  852.         if (getCollection(cls, key, result, defaultValue) == null) {
  853.             return null;
  854.         }
  855.         return result;
  856.     }

  857.     /**
  858.      * {@inheritDoc}
  859.      *
  860.      * @see #getStringArray(String)
  861.      */
  862.     @Override
  863.     public List<Object> getList(final String key) {
  864.         return getList(key, new ArrayList<>());
  865.     }

  866.     @Override
  867.     public List<Object> getList(final String key, final List<?> defaultValue) {
  868.         final Object value = getProperty(key);
  869.         final List<Object> list;

  870.         if (value instanceof String) {
  871.             list = new ArrayList<>(1);
  872.             list.add(interpolate((String) value));
  873.         } else if (value instanceof List) {
  874.             list = new ArrayList<>();
  875.             final List<?> l = (List<?>) value;

  876.             // add the interpolated elements in the new list
  877.             l.forEach(elem -> list.add(interpolate(elem)));
  878.         } else if (value == null) {
  879.             // This is okay because we just return this list to the caller
  880.             @SuppressWarnings("unchecked")
  881.             final List<Object> resultList = (List<Object>) defaultValue;
  882.             list = resultList;
  883.         } else if (value.getClass().isArray()) {
  884.             return Arrays.asList((Object[]) value);
  885.         } else if (isScalarValue(value)) {
  886.             return Collections.singletonList((Object) value.toString());
  887.         } else {
  888.             throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " + value.getClass().getName());
  889.         }
  890.         return list;
  891.     }

  892.     /**
  893.      * Gets the {@code ListDelimiterHandler} used by this instance.
  894.      *
  895.      * @return the {@code ListDelimiterHandler}
  896.      * @since 2.0
  897.      */
  898.     public ListDelimiterHandler getListDelimiterHandler() {
  899.         return listDelimiterHandler;
  900.     }

  901.     /**
  902.      * Gets the logger used by this configuration object.
  903.      *
  904.      * @return the logger
  905.      * @since 2.0
  906.      */
  907.     public ConfigurationLogger getLogger() {
  908.         return log;
  909.     }

  910.     @Override
  911.     public long getLong(final String key) {
  912.         final Long l = convert(Long.class, key, null, true);
  913.         return checkNonNullValue(key, l).longValue();
  914.     }

  915.     @Override
  916.     public long getLong(final String key, final long defaultValue) {
  917.         return getLong(key, Long.valueOf(defaultValue)).longValue();
  918.     }

  919.     @Override
  920.     public Long getLong(final String key, final Long defaultValue) {
  921.         return convert(Long.class, key, defaultValue, false);
  922.     }

  923.     @Override
  924.     public Properties getProperties(final String key) {
  925.         return getProperties(key, null);
  926.     }

  927.     /**
  928.      * Gets a list of properties associated with the given configuration key.
  929.      *
  930.      * @param key The configuration key.
  931.      * @param defaults Any default values for the returned {@code Properties} object. Ignored if {@code null}.
  932.      * @return The associated properties if key is found.
  933.      * @throws ConversionException is thrown if the key maps to an object that is not a String/List of Strings.
  934.      * @throws IllegalArgumentException if one of the tokens is malformed (does not contain an equals sign).
  935.      */
  936.     public Properties getProperties(final String key, final Properties defaults) {
  937.         /*
  938.          * Grab an array of the tokens for this key.
  939.          */
  940.         final String[] tokens = getStringArray(key);

  941.         /*
  942.          * Each token is of the form 'key=value'.
  943.          */
  944.         final Properties props = defaults == null ? new Properties() : new Properties(defaults);
  945.         for (final String token : tokens) {
  946.             final int equalSign = token.indexOf('=');
  947.             if (equalSign > 0) {
  948.                 final String pkey = token.substring(0, equalSign).trim();
  949.                 final String pvalue = token.substring(equalSign + 1).trim();
  950.                 props.put(pkey, pvalue);
  951.             } else if (tokens.length == 1 && StringUtils.isEmpty(key)) {
  952.                 // Semantically equivalent to an empty Properties
  953.                 // object.
  954.                 break;
  955.             } else {
  956.                 throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign");
  957.             }
  958.         }
  959.         return props;
  960.     }

  961.     /**
  962.      * {@inheritDoc} This implementation ensures proper synchronization. Subclasses have to define the abstract
  963.      * {@code getPropertyInternal()} method which is called from here.
  964.      */
  965.     @Override
  966.     public final Object getProperty(final String key) {
  967.         return syncRead(() -> getPropertyInternal(key), false);
  968.     }

  969.     /**
  970.      * Actually obtains the value of the specified property. This method is called by {@code getProperty()}. Concrete
  971.      * subclasses must define it to fetch the value of the desired property.
  972.      *
  973.      * @param key the key of the property in question
  974.      * @return the (raw) value of this property
  975.      * @since 2.0
  976.      */
  977.     protected abstract Object getPropertyInternal(String key);

  978.     @Override
  979.     public short getShort(final String key) {
  980.         final Short s = convert(Short.class, key, null, true);
  981.         return checkNonNullValue(key, s).shortValue();
  982.     }

  983.     @Override
  984.     public short getShort(final String key, final short defaultValue) {
  985.         return getShort(key, Short.valueOf(defaultValue)).shortValue();
  986.     }

  987.     @Override
  988.     public Short getShort(final String key, final Short defaultValue) {
  989.         return convert(Short.class, key, defaultValue, false);
  990.     }

  991.     /**
  992.      * {@inheritDoc}
  993.      *
  994.      * @see #setThrowExceptionOnMissing(boolean)
  995.      */
  996.     @Override
  997.     public String getString(final String key) {
  998.         return convert(String.class, key, null, true);
  999.     }

  1000.     @Override
  1001.     public String getString(final String key, final String defaultValue) {
  1002.         final String result = convert(String.class, key, null, false);
  1003.         return result != null ? result : interpolate(defaultValue);
  1004.     }

  1005.     /**
  1006.      * Gets an array of strings associated with the given configuration key. If the key doesn't map to an existing object, an
  1007.      * empty array is returned. When a property is added to a configuration, it is checked whether it contains multiple
  1008.      * values. This is obvious if the added object is a list or an array. For strings the association
  1009.      * {@link ListDelimiterHandler} is consulted to find out whether the string can be split into multiple values.
  1010.      *
  1011.      * @param key The configuration key.
  1012.      * @return The associated string array if key is found.
  1013.      * @throws ConversionException is thrown if the key maps to an object that is not a String/List of Strings.
  1014.      * @see #setListDelimiterHandler(ListDelimiterHandler)
  1015.      */
  1016.     @Override
  1017.     public String[] getStringArray(final String key) {
  1018.         final String[] result = (String[]) getArray(String.class, key);
  1019.         return result == null ? ArrayUtils.EMPTY_STRING_ARRAY : result;
  1020.     }

  1021.     /**
  1022.      * Gets the object responsible for synchronizing this configuration. All access to this configuration - both read and
  1023.      * write access - is controlled by this object. This implementation never returns <strong>null</strong>. If no
  1024.      * {@code Synchronizer} has been set, a {@link NoOpSynchronizer} is returned. So, per default, instances of
  1025.      * {@code AbstractConfiguration} are not thread-safe unless a suitable {@code Synchronizer} is set!
  1026.      *
  1027.      * @return the {@code Synchronizer} used by this instance
  1028.      * @since 2.0
  1029.      */
  1030.     @Override
  1031.     public final Synchronizer getSynchronizer() {
  1032.         return synchronizer;
  1033.     }

  1034.     @Override
  1035.     public ImmutableConfiguration immutableSubset(final String prefix) {
  1036.         return ConfigurationUtils.unmodifiableConfiguration(subset(prefix));
  1037.     }

  1038.     /**
  1039.      * Initializes the logger. Supports <strong>null</strong> input. This method can be called by derived classes in order to enable
  1040.      * logging.
  1041.      *
  1042.      * @param log the logger
  1043.      * @since 2.0
  1044.      */
  1045.     protected final void initLogger(final ConfigurationLogger log) {
  1046.         this.log = log != null ? log : ConfigurationLogger.newDummyLogger();
  1047.     }

  1048.     /**
  1049.      * Creates a default {@code ConfigurationInterpolator} which is initialized with all default {@code Lookup} objects.
  1050.      * This method is called by the constructor. It ensures that default interpolation works for every new configuration
  1051.      * instance.
  1052.      */
  1053.     private void installDefaultInterpolator() {
  1054.         installInterpolator(ConfigurationInterpolator.getDefaultPrefixLookups(), null);
  1055.     }

  1056.     /**
  1057.      * {@inheritDoc} This implementation creates a new {@code ConfigurationInterpolator} instance and initializes it with
  1058.      * the given {@code Lookup} objects. In addition, it adds a specialized default {@code Lookup} object which queries this
  1059.      * {@code Configuration}.
  1060.      *
  1061.      * @since 2.0
  1062.      */
  1063.     @Override
  1064.     public final void installInterpolator(final Map<String, ? extends Lookup> prefixLookups, final Collection<? extends Lookup> defLookups) {
  1065.         final InterpolatorSpecification spec = new InterpolatorSpecification.Builder().withPrefixLookups(prefixLookups).withDefaultLookups(defLookups)
  1066.             .withDefaultLookup(new ConfigurationLookup(this)).create();
  1067.         setInterpolator(ConfigurationInterpolator.fromSpecification(spec));
  1068.     }

  1069.     /**
  1070.      * Returns the interpolated value. This implementation delegates to the current {@code ConfigurationInterpolator}. If no
  1071.      * {@code ConfigurationInterpolator} is set, the passed in value is returned without changes.
  1072.      *
  1073.      * @param value the value to interpolate
  1074.      * @return the value with variables substituted
  1075.      */
  1076.     protected Object interpolate(final Object value) {
  1077.         final ConfigurationInterpolator ci = getInterpolator();
  1078.         return ci != null ? ci.interpolate(value) : value;
  1079.     }

  1080.     /**
  1081.      * interpolate key names to handle ${key} stuff
  1082.      *
  1083.      * @param base string to interpolate
  1084.      * @return the key name with the ${key} substituted
  1085.      */
  1086.     protected String interpolate(final String base) {
  1087.         return Objects.toString(interpolate((Object) base), null);
  1088.     }

  1089.     /**
  1090.      * Returns a configuration with the same content as this configuration, but with all variables replaced by their actual
  1091.      * values. This method tries to clone the configuration and then perform interpolation on all properties. So property
  1092.      * values of the form {@code ${var}} will be resolved as far as possible (if a variable cannot be resolved, it remains
  1093.      * unchanged). This operation is useful if the content of a configuration is to be exported or processed by an external
  1094.      * component that does not support variable interpolation.
  1095.      *
  1096.      * @return a configuration with all variables interpolated
  1097.      * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if this configuration cannot be cloned
  1098.      * @since 1.5
  1099.      */
  1100.     public Configuration interpolatedConfiguration() {
  1101.         // first clone this configuration
  1102.         final AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils.cloneConfiguration(this);

  1103.         // now perform interpolation
  1104.         c.setListDelimiterHandler(new DisabledListDelimiterHandler());
  1105.         getKeys().forEachRemaining(key -> c.setProperty(key, getList(key)));
  1106.         c.setListDelimiterHandler(getListDelimiterHandler());
  1107.         return c;
  1108.     }

  1109.     /**
  1110.      * {@inheritDoc} This implementation handles synchronization and delegates to {@code isEmptyInternal()}.
  1111.      */
  1112.     @Override
  1113.     public final boolean isEmpty() {
  1114.         return syncRead(() -> isEmptyInternal(), false);
  1115.     }

  1116.     /**
  1117.      * Actually checks whether this configuration contains data. This method is called by {@code isEmpty()}. It has to be
  1118.      * defined by concrete subclasses.
  1119.      *
  1120.      * @return <strong>true</strong> if this configuration contains no data, <strong>false</strong> otherwise
  1121.      * @since 2.0
  1122.      */
  1123.     protected abstract boolean isEmptyInternal();

  1124.     /**
  1125.      * Checks whether the specified object is a scalar value. This method is called by {@code getList()} and
  1126.      * {@code getStringArray()} if the property requested is not a string, a list, or an array. If it returns <strong>true</strong>,
  1127.      * the calling method transforms the value to a string and returns a list or an array with this single element. This
  1128.      * implementation returns <strong>true</strong> if the value is of a wrapper type for a primitive type.
  1129.      *
  1130.      * @param value the value to be checked
  1131.      * @return a flag whether the value is a scalar
  1132.      * @since 1.7
  1133.      */
  1134.     protected boolean isScalarValue(final Object value) {
  1135.         return ClassUtils.wrapperToPrimitive(value.getClass()) != null;
  1136.     }

  1137.     /**
  1138.      * Returns true if missing values throw Exceptions.
  1139.      *
  1140.      * @return true if missing values throw Exceptions
  1141.      */
  1142.     public boolean isThrowExceptionOnMissing() {
  1143.         return throwExceptionOnMissing;
  1144.     }

  1145.     /**
  1146.      * {@inheritDoc} This implementation delegates to {@code beginRead()} or {@code beginWrite()}, depending on the
  1147.      * {@code LockMode} argument. Subclasses can override these protected methods to perform additional steps when a
  1148.      * configuration is locked.
  1149.      *
  1150.      * @throws NullPointerException if the argument is <strong>null</strong>
  1151.      * @since 2.0
  1152.      */
  1153.     @Override
  1154.     public final void lock(final LockMode mode) {
  1155.         switch (mode) {
  1156.         case READ:
  1157.             beginRead(false);
  1158.             break;
  1159.         case WRITE:
  1160.             beginWrite(false);
  1161.             break;
  1162.         default:
  1163.             throw new IllegalArgumentException("Unsupported LockMode: " + mode);
  1164.         }
  1165.     }

  1166.     /**
  1167.      * Sets the {@code ConfigurationDecoder} for this configuration. This object is used by
  1168.      * {@link #getEncodedString(String)}.
  1169.      *
  1170.      * @param configurationDecoder the {@code ConfigurationDecoder}
  1171.      * @since 2.0
  1172.      */
  1173.     public void setConfigurationDecoder(final ConfigurationDecoder configurationDecoder) {
  1174.         this.configurationDecoder = configurationDecoder;
  1175.     }

  1176.     /**
  1177.      * Sets the {@code ConversionHandler} to be used by this instance. The {@code ConversionHandler} is responsible for
  1178.      * every kind of data type conversion. It is consulted by all get methods returning results in specific data types. A
  1179.      * newly created configuration uses a default {@code ConversionHandler} implementation. This can be changed while
  1180.      * initializing the configuration (for example via a builder). Note that access to this property is not synchronized.
  1181.      *
  1182.      * @param conversionHandler the {@code ConversionHandler} to be used (must not be <strong>null</strong>)
  1183.      * @throws IllegalArgumentException if the {@code ConversionHandler} is <strong>null</strong>
  1184.      * @since 2.0
  1185.      */
  1186.     public void setConversionHandler(final ConversionHandler conversionHandler) {
  1187.         if (conversionHandler == null) {
  1188.             throw new IllegalArgumentException("ConversionHandler must not be null!");
  1189.         }
  1190.         this.conversionHandler = conversionHandler;
  1191.     }

  1192.     /**
  1193.      * Adds all {@code Lookup} objects in the given collection as default lookups (i.e. lookups without a variable prefix)
  1194.      * to the {@code ConfigurationInterpolator} object of this configuration. In addition, it adds a specialized default
  1195.      * {@code Lookup} object which queries this {@code Configuration}. The set of {@code Lookup} objects with prefixes is
  1196.      * not modified by this method. If this configuration does not have a {@code ConfigurationInterpolator}, a new instance
  1197.      * is created. Note: This method is mainly intended to be used for initializing a configuration when it is created by a
  1198.      * builder. Normal client code should better call {@link #installInterpolator(Map, Collection)} to define the
  1199.      * {@code ConfigurationInterpolator} in a single step.
  1200.      *
  1201.      * @param lookups the collection with default {@code Lookup} objects to be added
  1202.      * @since 2.0
  1203.      */
  1204.     public void setDefaultLookups(final Collection<? extends Lookup> lookups) {
  1205.         boolean success;
  1206.         do {
  1207.             final ConfigurationInterpolator ciOld = getInterpolator();
  1208.             final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator();
  1209.             Lookup confLookup = findConfigurationLookup(ciNew);
  1210.             if (confLookup == null) {
  1211.                 confLookup = new ConfigurationLookup(this);
  1212.             } else {
  1213.                 ciNew.removeDefaultLookup(confLookup);
  1214.             }
  1215.             ciNew.addDefaultLookups(lookups);
  1216.             ciNew.addDefaultLookup(confLookup);
  1217.             success = interpolator.compareAndSet(ciOld, ciNew);
  1218.         } while (!success);
  1219.     }

  1220.     /**
  1221.      * {@inheritDoc} This implementation sets the passed in object without further modifications. A <strong>null</strong> argument is
  1222.      * allowed; this disables interpolation.
  1223.      *
  1224.      * @since 2.0
  1225.      */
  1226.     @Override
  1227.     public final void setInterpolator(final ConfigurationInterpolator ci) {
  1228.         interpolator.set(ci);
  1229.     }

  1230.     /**
  1231.      * <p>
  1232.      * Sets the {@code ListDelimiterHandler} to be used by this instance. This object is invoked every time when dealing
  1233.      * with string properties that may contain a list delimiter and thus have to be split to multiple values. Per default, a
  1234.      * {@code ListDelimiterHandler} implementation is set which does not support list splitting. This can be changed for
  1235.      * instance by setting a {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler
  1236.      * DefaultListDelimiterHandler} object.
  1237.      * </p>
  1238.      * <p>
  1239.      * <strong>Warning:</strong> Be careful when changing the list delimiter handler when the configuration has already been
  1240.      * loaded/populated. List handling is typically applied already when properties are added to the configuration. If later
  1241.      * another handler is set which processes lists differently, results may be unexpected; some operations may even cause
  1242.      * exceptions.
  1243.      * </p>
  1244.      *
  1245.      * @param listDelimiterHandler the {@code ListDelimiterHandler} to be used (must not be <strong>null</strong>)
  1246.      * @throws IllegalArgumentException if the {@code ListDelimiterHandler} is <strong>null</strong>
  1247.      * @since 2.0
  1248.      */
  1249.     public void setListDelimiterHandler(final ListDelimiterHandler listDelimiterHandler) {
  1250.         if (listDelimiterHandler == null) {
  1251.             throw new IllegalArgumentException("List delimiter handler must not be null!");
  1252.         }
  1253.         this.listDelimiterHandler = listDelimiterHandler;
  1254.     }

  1255.     /**
  1256.      * Allows setting the logger to be used by this configuration object. This method makes it possible for clients to
  1257.      * exactly control logging behavior. Per default a logger is set that will ignore all log messages. Derived classes that
  1258.      * want to enable logging should call this method during their initialization with the logger to be used. It is legal to
  1259.      * pass a <strong>null</strong> logger; in this case, logging will be disabled.
  1260.      *
  1261.      * @param log the new logger
  1262.      * @since 2.0
  1263.      */
  1264.     public void setLogger(final ConfigurationLogger log) {
  1265.         initLogger(log);
  1266.     }

  1267.     /**
  1268.      * Sets the specified {@code ConfigurationInterpolator} as the parent of this configuration's
  1269.      * {@code ConfigurationInterpolator}. If this configuration does not have a {@code ConfigurationInterpolator}, a new
  1270.      * instance is created. Note: This method is mainly intended to be used for initializing a configuration when it is
  1271.      * created by a builder. Normal client code can directly update the {@code ConfigurationInterpolator}.
  1272.      *
  1273.      * @param parent the parent {@code ConfigurationInterpolator} to be set
  1274.      * @since 2.0
  1275.      */
  1276.     public void setParentInterpolator(final ConfigurationInterpolator parent) {
  1277.         boolean success;
  1278.         do {
  1279.             final ConfigurationInterpolator ciOld = getInterpolator();
  1280.             final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator();
  1281.             ciNew.setParentInterpolator(parent);
  1282.             success = interpolator.compareAndSet(ciOld, ciNew);
  1283.         } while (!success);
  1284.     }

  1285.     /**
  1286.      * Registers all {@code Lookup} objects in the given map at the current {@code ConfigurationInterpolator} of this
  1287.      * configuration. The set of default lookup objects (for variables without a prefix) is not modified by this method. If
  1288.      * this configuration does not have a {@code ConfigurationInterpolator}, a new instance is created. Note: This method is
  1289.      * mainly intended to be used for initializing a configuration when it is created by a builder. Normal client code
  1290.      * should better call {@link #installInterpolator(Map, Collection)} to define the {@code ConfigurationInterpolator} in a
  1291.      * single step.
  1292.      *
  1293.      * @param lookups a map with new {@code Lookup} objects and their prefixes (may be <strong>null</strong>)
  1294.      * @since 2.0
  1295.      */
  1296.     public void setPrefixLookups(final Map<String, ? extends Lookup> lookups) {
  1297.         boolean success;
  1298.         do {
  1299.             // do this in a loop because the ConfigurationInterpolator
  1300.             // instance may be changed by another thread
  1301.             final ConfigurationInterpolator ciOld = getInterpolator();
  1302.             final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator();
  1303.             ciNew.registerLookups(lookups);
  1304.             success = interpolator.compareAndSet(ciOld, ciNew);
  1305.         } while (!success);
  1306.     }

  1307.     @Override
  1308.     public final void setProperty(final String key, final Object value) {
  1309.         syncWrite(() -> {
  1310.             fireEvent(ConfigurationEvent.SET_PROPERTY, key, value, true);
  1311.             setPropertyInternal(key, value);
  1312.             fireEvent(ConfigurationEvent.SET_PROPERTY, key, value, false);
  1313.         }, false);
  1314.     }

  1315.     /**
  1316.      * Actually sets the value of a property. This method is called by {@code setProperty()}. It provides a default
  1317.      * implementation of this functionality by clearing the specified key and delegating to {@code addProperty()}.
  1318.      * Subclasses should override this method if they can provide a more efficient algorithm for setting a property value.
  1319.      *
  1320.      * @param key the property key
  1321.      * @param value the new property value
  1322.      * @since 2.0
  1323.      */
  1324.     protected void setPropertyInternal(final String key, final Object value) {
  1325.         setDetailEvents(false);
  1326.         try {
  1327.             clearProperty(key);
  1328.             addProperty(key, value);
  1329.         } finally {
  1330.             setDetailEvents(true);
  1331.         }
  1332.     }

  1333.     /**
  1334.      * Sets the object responsible for synchronizing this configuration. This method has to be called with a suitable
  1335.      * {@code Synchronizer} object when initializing this configuration instance in order to make it thread-safe.
  1336.      *
  1337.      * @param synchronizer the new {@code Synchronizer}; can be <strong>null</strong>, then this instance uses a
  1338.      *        {@link NoOpSynchronizer}
  1339.      * @since 2.0
  1340.      */
  1341.     @Override
  1342.     public final void setSynchronizer(final Synchronizer synchronizer) {
  1343.         this.synchronizer = synchronizer != null ? synchronizer : NoOpSynchronizer.INSTANCE;
  1344.     }

  1345.     /**
  1346.      * Allows to set the {@code throwExceptionOnMissing} flag. This flag controls the behavior of property getter methods
  1347.      * that return objects if the requested property is missing. If the flag is set to <strong>false</strong> (which is the default
  1348.      * value), these methods will return <strong>null</strong>. If set to <strong>true</strong>, they will throw a
  1349.      * {@code NoSuchElementException} exception. Note that getter methods for primitive data types are not affected by this
  1350.      * flag.
  1351.      *
  1352.      * @param throwExceptionOnMissing The new value for the property
  1353.      */
  1354.     public void setThrowExceptionOnMissing(final boolean throwExceptionOnMissing) {
  1355.         this.throwExceptionOnMissing = throwExceptionOnMissing;
  1356.     }

  1357.     /**
  1358.      * {@inheritDoc} This implementation handles synchronization and delegates to {@code sizeInternal()}.
  1359.      */
  1360.     @Override
  1361.     public final int size() {
  1362.         return syncRead(this::sizeInternal, false);
  1363.     }

  1364.     /**
  1365.      * Actually calculates the size of this configuration. This method is called by {@code size()} with a read lock held.
  1366.      * The base implementation provided here calculates the size based on the iterator returned by {@code getKeys()}. Sub
  1367.      * classes which can determine the size in a more efficient way should override this method.
  1368.      *
  1369.      * @return the size of this configuration (i.e. the number of keys)
  1370.      */
  1371.     protected int sizeInternal() {
  1372.         int size = 0;
  1373.         for (final Iterator<String> keyIt = getKeysInternal(); keyIt.hasNext(); size++) {
  1374.             keyIt.next();
  1375.         }
  1376.         return size;
  1377.     }

  1378.     @Override
  1379.     public Configuration subset(final String prefix) {
  1380.         return new SubsetConfiguration(this, prefix, DELIMITER);
  1381.     }

  1382.     <T, E extends Throwable> T syncRead(final FailableSupplier<T, E> supplier, final boolean optimize) throws E {
  1383.         beginRead(optimize);
  1384.         try {
  1385.             return supplier.get();
  1386.         } finally {
  1387.             endRead();
  1388.         }
  1389.     }

  1390.     void syncRead(final Runnable runnable, final boolean optimize) {
  1391.         beginRead(optimize);
  1392.         try {
  1393.             runnable.run();
  1394.         } finally {
  1395.             endRead();
  1396.         }
  1397.     }

  1398.     <T> T syncReadValue(final T value, final boolean optimize) {
  1399.         beginRead(optimize);
  1400.         try {
  1401.             return value;
  1402.         } finally {
  1403.             endRead();
  1404.         }
  1405.     }

  1406.     <E extends Throwable> void syncWrite(final FailableRunnable<E> runnable, final boolean optimize) throws E {
  1407.         beginWrite(optimize);
  1408.         try {
  1409.             runnable.run();
  1410.         } finally {
  1411.             endWrite();
  1412.         }
  1413.     }

  1414.     /**
  1415.      * {@inheritDoc} This implementation delegates to {@code endRead()} or {@code endWrite()}, depending on the
  1416.      * {@code LockMode} argument. Subclasses can override these protected methods to perform additional steps when a
  1417.      * configuration's lock is released.
  1418.      *
  1419.      * @throws NullPointerException if the argument is <strong>null</strong>
  1420.      */
  1421.     @Override
  1422.     public final void unlock(final LockMode mode) {
  1423.         switch (mode) {
  1424.         case READ:
  1425.             endRead();
  1426.             break;
  1427.         case WRITE:
  1428.             endWrite();
  1429.             break;
  1430.         default:
  1431.             throw new IllegalArgumentException("Unsupported LockMode: " + mode);
  1432.         }
  1433.     }
  1434. }