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