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