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