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