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 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 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.getKeys().forEachRemaining(key -> addProperty(key, encodeForCopy(configuration.getProperty(key)))); 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 * @since 2.11.0 445 */ 446 @Override 447 public final boolean containsValue(final Object value) { 448 return syncRead(() -> containsValueInternal(value), false); 449 } 450 451 /** 452 * 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 453 * {@link #containsKeyInternal containsKey} method. 454 * <p> 455 * The implementation of this method will be different depending on the type of Configuration used. 456 * </p> 457 * 458 * <p> 459 * Note that this method is identical in functionality to {@link #containsValue containsValue}, (which is part of the {@link ImmutableConfiguration} 460 * interface). 461 * </p> 462 * 463 * @param value the value in question 464 * @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; 465 * {@code false} otherwise. 466 * @since 2.11.0 467 */ 468 protected abstract boolean containsValueInternal(Object value); 469 470 /** 471 * Helper method for obtaining a property value with a type conversion. 472 * 473 * @param <T> the target type of the conversion 474 * @param cls the target class 475 * @param key the key of the desired property 476 * @param defValue a default value 477 * @param throwOnMissing a flag whether an exception should be thrown for a missing value 478 * @return the converted value 479 */ 480 private <T> T convert(final Class<T> cls, final String key, final T defValue, final boolean throwOnMissing) { 481 if (cls.isArray()) { 482 return cls.cast(convertToArray(cls.getComponentType(), key, defValue)); 483 } 484 485 final T result = getAndConvertProperty(cls, key, defValue); 486 if (result == null) { 487 if (throwOnMissing && isThrowExceptionOnMissing()) { 488 throwMissingPropertyException(key); 489 } 490 return defValue; 491 } 492 493 return result; 494 } 495 496 /** 497 * Performs a conversion to an array result class. This implementation delegates to the {@link ConversionHandler} to 498 * perform the actual type conversion. If this results in a <strong>null</strong> result (because the property is undefined), the 499 * default value is returned. It is checked whether the default value is an array with the correct component type. If 500 * not, an exception is thrown. 501 * 502 * @param cls the component class of the array 503 * @param key the configuration key 504 * @param defaultValue an optional default value 505 * @return the converted array 506 * @throws IllegalArgumentException if the default value is not a compatible array 507 */ 508 private Object convertToArray(final Class<?> cls, final String key, final Object defaultValue) { 509 checkDefaultValueArray(cls, defaultValue); 510 return ObjectUtils.defaultIfNull(getConversionHandler().toArray(getProperty(key), cls, getInterpolator()), defaultValue); 511 } 512 513 /** 514 * Copies the content of the specified configuration into this configuration. If the specified configuration contains a 515 * key that is also present in this configuration, the value of this key will be replaced by the new value. 516 * <em>Note:</em> This method won't work well when copying hierarchical configurations because it is not able to copy 517 * information about the properties' structure (i.e. the parent-child-relationships will get lost). So when dealing with 518 * hierarchical configuration objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be 519 * used. 520 * 521 * @param configuration the configuration to copy (can be <strong>null</strong>, then this operation will have no effect) 522 * @since 1.5 523 */ 524 public void copy(final Configuration configuration) { 525 if (configuration != null) { 526 configuration.lock(LockMode.READ); 527 try { 528 configuration.getKeys().forEachRemaining(key -> setProperty(key, encodeForCopy(configuration.getProperty(key)))); 529 } finally { 530 configuration.unlock(LockMode.READ); 531 } 532 } 533 } 534 535 /** 536 * Encodes a property value so that it can be added to this configuration. This method deals with list delimiters. The 537 * passed in object has to be escaped so that an add operation yields the same result. If it is a list, all of its 538 * values have to be escaped. 539 * 540 * @param value the value to be encoded 541 * @return the encoded value 542 */ 543 private Object encodeForCopy(final Object value) { 544 if (value instanceof Collection) { 545 return encodeListForCopy((Collection<?>) value); 546 } 547 return getListDelimiterHandler().escape(value, ListDelimiterHandler.NOOP_TRANSFORMER); 548 } 549 550 /** 551 * Encodes a list with property values so that it can be added to this configuration. This method calls 552 * {@code encodeForCopy()} for all list elements. 553 * 554 * @param values the list to be encoded 555 * @return a list with encoded elements 556 */ 557 private Object encodeListForCopy(final Collection<?> values) { 558 return values.stream().map(this::encodeForCopy).collect(Collectors.toList()); 559 } 560 561 /** 562 * Notifies this configuration's {@link Synchronizer} that a read operation has finished. This method is called by all 563 * methods which access this configuration in a read-only manner at the end of their execution. Subclasses may override 564 * it to perform additional actions after this read operation. <strong>In any case the inherited method must be called! 565 * Otherwise, the read lock will not be released.</strong> 566 * 567 * @since 2.0 568 */ 569 protected void endRead() { 570 getSynchronizer().endRead(); 571 } 572 573 /** 574 * Notifies this configuration's {@link Synchronizer} that an update operation has finished. This method is called by 575 * all methods which modify this configuration at the end of their execution. Subclasses may override it to perform 576 * additional operations after an update. <strong>In any case the inherited method must be called! Otherwise, the write 577 * lock will not be released.</strong> 578 * 579 * @since 2.0 580 */ 581 protected void endWrite() { 582 getSynchronizer().endWrite(); 583 } 584 585 /** 586 * Finds a {@code ConfigurationLookup} pointing to this configuration in the default lookups of the specified 587 * {@code ConfigurationInterpolator}. This method is called to ensure that there is exactly one default lookup querying 588 * this configuration. 589 * 590 * @param ci the {@code ConfigurationInterpolator} in question 591 * @return the found {@code Lookup} object or <strong>null</strong> 592 */ 593 private Lookup findConfigurationLookup(final ConfigurationInterpolator ci) { 594 return findConfigurationLookup(ci, this); 595 } 596 597 @Override 598 public <T> T get(final Class<T> cls, final String key) { 599 return convert(cls, key, null, true); 600 } 601 602 /** 603 * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion. 604 */ 605 @Override 606 public <T> T get(final Class<T> cls, final String key, final T defaultValue) { 607 return convert(cls, key, defaultValue, false); 608 } 609 610 /** 611 * Obtains the property value for the specified key and converts it to the given target class. 612 * 613 * @param <T> the target type of the conversion 614 * @param cls the target class 615 * @param key the key of the desired property 616 * @param defaultValue a default value 617 * @return the converted value of this property 618 * @throws ConversionException if the conversion cannot be performed 619 */ 620 private <T> T getAndConvertProperty(final Class<T> cls, final String key, final T defaultValue) { 621 final Object value = getProperty(key); 622 try { 623 return ObjectUtils.defaultIfNull(getConversionHandler().to(value, cls, getInterpolator()), defaultValue); 624 } catch (final ConversionException cex) { 625 // improve error message 626 throw new ConversionException(String.format("Key '%s' cannot be converted to class %s. Value is: '%s'.", key, cls.getName(), String.valueOf(value)), 627 cex.getCause()); 628 } 629 } 630 631 @Override 632 public Object getArray(final Class<?> cls, final String key) { 633 return getArray(cls, key, null); 634 } 635 636 /** 637 * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion. 638 * If this results in a <strong>null</strong> result (because the property is undefined), the default value is returned. It is 639 * checked whether the default value is an array with the correct component type. If not, an exception is thrown. 640 * 641 * @throws IllegalArgumentException if the default value is not a compatible array 642 */ 643 @Override 644 public Object getArray(final Class<?> cls, final String key, final Object defaultValue) { 645 return convertToArray(cls, key, defaultValue); 646 } 647 648 /** 649 * {@inheritDoc} 650 * 651 * @see #setThrowExceptionOnMissing(boolean) 652 */ 653 @Override 654 public BigDecimal getBigDecimal(final String key) { 655 return convert(BigDecimal.class, key, null, true); 656 } 657 658 @Override 659 public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) { 660 return convert(BigDecimal.class, key, defaultValue, false); 661 } 662 663 /** 664 * {@inheritDoc} 665 * 666 * @see #setThrowExceptionOnMissing(boolean) 667 */ 668 @Override 669 public BigInteger getBigInteger(final String key) { 670 return convert(BigInteger.class, key, null, true); 671 } 672 673 @Override 674 public BigInteger getBigInteger(final String key, final BigInteger defaultValue) { 675 return convert(BigInteger.class, key, defaultValue, false); 676 } 677 678 @Override 679 public boolean getBoolean(final String key) { 680 final Boolean b = convert(Boolean.class, key, null, true); 681 return checkNonNullValue(key, b).booleanValue(); 682 } 683 684 @Override 685 public boolean getBoolean(final String key, final boolean defaultValue) { 686 return getBoolean(key, Boolean.valueOf(defaultValue)).booleanValue(); 687 } 688 689 /** 690 * Obtains the value of the specified key and tries to convert it into a {@code Boolean} object. If the property has no 691 * value, the passed in default value will be used. 692 * 693 * @param key the key of the property 694 * @param defaultValue the default value 695 * @return the value of this key converted to a {@code Boolean} 696 * @throws ConversionException if the value cannot be converted to a {@code Boolean} 697 */ 698 @Override 699 public Boolean getBoolean(final String key, final Boolean defaultValue) { 700 return convert(Boolean.class, key, defaultValue, false); 701 } 702 703 @Override 704 public byte getByte(final String key) { 705 final Byte b = convert(Byte.class, key, null, true); 706 return checkNonNullValue(key, b).byteValue(); 707 } 708 709 @Override 710 public byte getByte(final String key, final byte defaultValue) { 711 return getByte(key, Byte.valueOf(defaultValue)).byteValue(); 712 } 713 714 @Override 715 public Byte getByte(final String key, final Byte defaultValue) { 716 return convert(Byte.class, key, defaultValue, false); 717 } 718 719 @Override 720 public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target) { 721 return getCollection(cls, key, target, null); 722 } 723 724 /** 725 * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual conversion. If no 726 * target collection is provided, an {@code ArrayList} is created. 727 */ 728 @Override 729 public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target, final Collection<T> defaultValue) { 730 final Object src = getProperty(key); 731 if (src == null) { 732 return handleDefaultCollection(target, defaultValue); 733 } 734 735 final Collection<T> targetCol = target != null ? target : new ArrayList<>(); 736 getConversionHandler().toCollection(src, cls, getInterpolator(), targetCol); 737 return targetCol; 738 } 739 740 /** 741 * Gets the {@code ConfigurationDecoder} used by this instance. 742 * 743 * @return the {@code ConfigurationDecoder} 744 * @since 2.0 745 */ 746 public ConfigurationDecoder getConfigurationDecoder() { 747 return configurationDecoder; 748 } 749 750 /** 751 * Gets the {@code ConversionHandler} used by this instance. 752 * 753 * @return the {@code ConversionHandler} 754 * @since 2.0 755 */ 756 public ConversionHandler getConversionHandler() { 757 return conversionHandler; 758 } 759 760 @Override 761 public double getDouble(final String key) { 762 final Double d = convert(Double.class, key, null, true); 763 return checkNonNullValue(key, d).doubleValue(); 764 } 765 766 @Override 767 public double getDouble(final String key, final double defaultValue) { 768 return getDouble(key, Double.valueOf(defaultValue)).doubleValue(); 769 } 770 771 @Override 772 public Double getDouble(final String key, final Double defaultValue) { 773 return convert(Double.class, key, defaultValue, false); 774 } 775 776 @Override 777 public Duration getDuration(final String key) { 778 return checkNonNullValue(key, convert(Duration.class, key, null, true)); 779 } 780 781 @Override 782 public Duration getDuration(final String key, final Duration defaultValue) { 783 return convert(Duration.class, key, defaultValue, false); 784 } 785 786 /** 787 * {@inheritDoc} This implementation makes use of the {@code ConfigurationDecoder} set for this configuration. If no 788 * such object has been set, an {@code IllegalStateException} exception is thrown. 789 * 790 * @throws IllegalStateException if no {@code ConfigurationDecoder} is set 791 * @see #setConfigurationDecoder(ConfigurationDecoder) 792 */ 793 @Override 794 public String getEncodedString(final String key) { 795 final ConfigurationDecoder decoder = getConfigurationDecoder(); 796 if (decoder == null) { 797 throw new IllegalStateException("No default ConfigurationDecoder defined!"); 798 } 799 return getEncodedString(key, decoder); 800 } 801 802 /** 803 * {@inheritDoc} This implementation delegates to {@link #getString(String)} in order to obtain the value of the passed 804 * in key. This value is passed to the decoder. Because {@code getString()} is used behind the scenes all standard 805 * features like handling of missing keys and interpolation work as expected. 806 */ 807 @Override 808 public String getEncodedString(final String key, final ConfigurationDecoder decoder) { 809 if (decoder == null) { 810 throw new IllegalArgumentException("ConfigurationDecoder must not be null!"); 811 } 812 813 final String value = getString(key); 814 return value != null ? decoder.decode(value) : null; 815 } 816 817 @Override 818 public float getFloat(final String key) { 819 final Float f = convert(Float.class, key, null, true); 820 return checkNonNullValue(key, f).floatValue(); 821 } 822 823 @Override 824 public float getFloat(final String key, final float defaultValue) { 825 return getFloat(key, Float.valueOf(defaultValue)).floatValue(); 826 } 827 828 @Override 829 public Float getFloat(final String key, final Float defaultValue) { 830 return convert(Float.class, key, defaultValue, false); 831 } 832 833 @Override 834 public int getInt(final String key) { 835 final Integer i = convert(Integer.class, key, null, true); 836 return checkNonNullValue(key, i).intValue(); 837 } 838 839 @Override 840 public int getInt(final String key, final int defaultValue) { 841 return getInteger(key, Integer.valueOf(defaultValue)).intValue(); 842 } 843 844 @Override 845 public Integer getInteger(final String key, final Integer defaultValue) { 846 return convert(Integer.class, key, defaultValue, false); 847 } 848 849 /** 850 * Gets the {@code ConfigurationInterpolator} object that manages the lookup objects for resolving variables. 851 * Unless a custom interpolator has been set or the instance has been modified, the returned interpolator will 852 * resolve values from this configuration instance and support the 853 * {@link ConfigurationInterpolator#getDefaultPrefixLookups() default prefix lookups}. 854 * 855 * @return the {@code ConfigurationInterpolator} associated with this configuration 856 * @since 1.4 857 * @see ConfigurationInterpolator#getDefaultPrefixLookups() 858 */ 859 @Override 860 public ConfigurationInterpolator getInterpolator() { 861 return interpolator.get(); 862 } 863 864 /** 865 * {@inheritDoc} This implementation takes care of synchronization and then delegates to {@code getKeysInternal()} for 866 * obtaining the actual iterator. Note that depending on a concrete implementation, an iteration may fail if the 867 * configuration is updated concurrently. 868 */ 869 @Override 870 public final Iterator<String> getKeys() { 871 return syncRead(() -> getKeysInternal(), false); 872 } 873 874 /** 875 * {@inheritDoc} This implementation returns keys that either match the prefix or start with the prefix followed by a 876 * dot ('.'). So the call {@code getKeys("db");} will find the keys {@code db}, {@code db.user}, or {@code db.password}, 877 * but not the key {@code dbdriver}. 878 */ 879 @Override 880 public final Iterator<String> getKeys(final String prefix) { 881 return syncRead(() -> getKeysInternal(prefix), false); 882 } 883 884 /** 885 * {@inheritDoc} This implementation returns keys that either match the prefix or start with the prefix followed by the delimiter. 886 * So the call {@code getKeys("db");} will find the keys {@code db}, {@code db@user}, or {@code db@password}, 887 * but not the key {@code dbdriver}. 888 */ 889 @Override 890 public final Iterator<String> getKeys(final String prefix, final String delimiter) { 891 return syncRead(() -> getKeysInternal(prefix, delimiter), false); 892 } 893 894 /** 895 * Actually creates an iterator for iterating over the keys in this configuration. This method is called by 896 * {@code getKeys()}, it has to be defined by concrete subclasses. 897 * 898 * @return an {@code Iterator} with all property keys in this configuration 899 * @since 2.0 900 */ 901 protected abstract Iterator<String> getKeysInternal(); 902 903 /** 904 * Gets an {@code Iterator} with all property keys starting with the specified prefix. This method is called by 905 * {@link #getKeys(String)}. It is fully implemented by delegating to {@code getKeysInternal()} and returning a special 906 * iterator which filters for the passed in prefix. Subclasses can override it if they can provide a more efficient way 907 * to iterate over specific keys only. 908 * 909 * @param prefix the prefix for the keys to be taken into account 910 * @return an {@code Iterator} returning the filtered keys 911 * @since 2.0 912 */ 913 protected Iterator<String> getKeysInternal(final String prefix) { 914 return new PrefixedKeysIterator(getKeysInternal(), prefix); 915 } 916 917 /** 918 * Gets an {@code Iterator} with all property keys starting with the specified prefix and specified delimiter. This method is called by 919 * {@link #getKeys(String)}. It is fully implemented by delegating to {@code getKeysInternal()} and returning a special 920 * iterator which filters for the passed in prefix. Subclasses can override it if they can provide a more efficient way 921 * to iterate over specific keys only. 922 * 923 * @param prefix the prefix for the keys to be taken into account 924 * @param delimiter the prefix delimiter 925 * @return an {@code Iterator} returning the filtered keys 926 * @since 2.10.0 927 */ 928 protected Iterator<String> getKeysInternal(final String prefix, final String delimiter) { 929 return new PrefixedKeysIterator(getKeysInternal(), prefix, delimiter); 930 } 931 932 @Override 933 public <T> List<T> getList(final Class<T> cls, final String key) { 934 return getList(cls, key, null); 935 } 936 937 /** 938 * {@inheritDoc} This implementation delegates to the generic {@code getCollection()}. As target collection a newly 939 * created {@code ArrayList} is passed in. 940 */ 941 @Override 942 public <T> List<T> getList(final Class<T> cls, final String key, final List<T> defaultValue) { 943 final List<T> result = new ArrayList<>(); 944 if (getCollection(cls, key, result, defaultValue) == null) { 945 return null; 946 } 947 return result; 948 } 949 950 /** 951 * {@inheritDoc} 952 * 953 * @see #getStringArray(String) 954 */ 955 @Override 956 public List<Object> getList(final String key) { 957 return getList(key, new ArrayList<>()); 958 } 959 960 @Override 961 public List<Object> getList(final String key, final List<?> defaultValue) { 962 final Object value = getProperty(key); 963 final List<Object> list; 964 965 if (value instanceof String) { 966 list = new ArrayList<>(1); 967 list.add(interpolate((String) value)); 968 } else if (value instanceof List) { 969 list = new ArrayList<>(); 970 final List<?> l = (List<?>) value; 971 972 // add the interpolated elements in the new list 973 l.forEach(elem -> list.add(interpolate(elem))); 974 } else if (value == null) { 975 // This is okay because we just return this list to the caller 976 @SuppressWarnings("unchecked") 977 final List<Object> resultList = (List<Object>) defaultValue; 978 list = resultList; 979 } else if (value.getClass().isArray()) { 980 return Arrays.asList((Object[]) value); 981 } else if (isScalarValue(value)) { 982 return Collections.singletonList((Object) value.toString()); 983 } else { 984 throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " + value.getClass().getName()); 985 } 986 return list; 987 } 988 989 /** 990 * Gets the {@code ListDelimiterHandler} used by this instance. 991 * 992 * @return the {@code ListDelimiterHandler} 993 * @since 2.0 994 */ 995 public ListDelimiterHandler getListDelimiterHandler() { 996 return listDelimiterHandler; 997 } 998 999 /** 1000 * Gets the logger used by this configuration object. 1001 * 1002 * @return the logger 1003 * @since 2.0 1004 */ 1005 public ConfigurationLogger getLogger() { 1006 return log; 1007 } 1008 1009 @Override 1010 public long getLong(final String key) { 1011 final Long l = convert(Long.class, key, null, true); 1012 return checkNonNullValue(key, l).longValue(); 1013 } 1014 1015 @Override 1016 public long getLong(final String key, final long defaultValue) { 1017 return getLong(key, Long.valueOf(defaultValue)).longValue(); 1018 } 1019 1020 @Override 1021 public Long getLong(final String key, final Long defaultValue) { 1022 return convert(Long.class, key, defaultValue, false); 1023 } 1024 1025 @Override 1026 public Properties getProperties(final String key) { 1027 return getProperties(key, null); 1028 } 1029 1030 /** 1031 * Gets a list of properties associated with the given configuration key. 1032 * 1033 * @param key The configuration key. 1034 * @param defaults Any default values for the returned {@code Properties} object. Ignored if {@code null}. 1035 * @return The associated properties if key is found. 1036 * @throws ConversionException is thrown if the key maps to an object that is not a String/List of Strings. 1037 * @throws IllegalArgumentException if one of the tokens is malformed (does not contain an equals sign). 1038 */ 1039 public Properties getProperties(final String key, final Properties defaults) { 1040 /* 1041 * Grab an array of the tokens for this key. 1042 */ 1043 final String[] tokens = getStringArray(key); 1044 1045 /* 1046 * Each token is of the form 'key=value'. 1047 */ 1048 final Properties props = defaults == null ? new Properties() : new Properties(defaults); 1049 for (final String token : tokens) { 1050 final int equalSign = token.indexOf('='); 1051 if (equalSign > 0) { 1052 final String pkey = token.substring(0, equalSign).trim(); 1053 final String pvalue = token.substring(equalSign + 1).trim(); 1054 props.put(pkey, pvalue); 1055 } else if (tokens.length == 1 && StringUtils.isEmpty(key)) { 1056 // Semantically equivalent to an empty Properties 1057 // object. 1058 break; 1059 } else { 1060 throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign"); 1061 } 1062 } 1063 return props; 1064 } 1065 1066 /** 1067 * {@inheritDoc} This implementation ensures proper synchronization. Subclasses have to define the abstract 1068 * {@code getPropertyInternal()} method which is called from here. 1069 */ 1070 @Override 1071 public final Object getProperty(final String key) { 1072 return syncRead(() -> getPropertyInternal(key), false); 1073 } 1074 1075 /** 1076 * Actually obtains the value of the specified property. This method is called by {@code getProperty()}. Concrete 1077 * subclasses must define it to fetch the value of the desired property. 1078 * 1079 * @param key the key of the property in question 1080 * @return the (raw) value of this property 1081 * @since 2.0 1082 */ 1083 protected abstract Object getPropertyInternal(String key); 1084 1085 @Override 1086 public short getShort(final String key) { 1087 final Short s = convert(Short.class, key, null, true); 1088 return checkNonNullValue(key, s).shortValue(); 1089 } 1090 1091 @Override 1092 public short getShort(final String key, final short defaultValue) { 1093 return getShort(key, Short.valueOf(defaultValue)).shortValue(); 1094 } 1095 1096 @Override 1097 public Short getShort(final String key, final Short defaultValue) { 1098 return convert(Short.class, key, defaultValue, false); 1099 } 1100 1101 /** 1102 * {@inheritDoc} 1103 * 1104 * @see #setThrowExceptionOnMissing(boolean) 1105 */ 1106 @Override 1107 public String getString(final String key) { 1108 return convert(String.class, key, null, true); 1109 } 1110 1111 @Override 1112 public String getString(final String key, final String defaultValue) { 1113 final String result = convert(String.class, key, null, false); 1114 return result != null ? result : interpolate(defaultValue); 1115 } 1116 1117 /** 1118 * Gets an array of strings associated with the given configuration key. If the key doesn't map to an existing object, an 1119 * empty array is returned. When a property is added to a configuration, it is checked whether it contains multiple 1120 * values. This is obvious if the added object is a list or an array. For strings the association 1121 * {@link ListDelimiterHandler} is consulted to find out whether the string can be split into multiple values. 1122 * 1123 * @param key The configuration key. 1124 * @return The associated string array if key is found. 1125 * @throws ConversionException is thrown if the key maps to an object that is not a String/List of Strings. 1126 * @see #setListDelimiterHandler(ListDelimiterHandler) 1127 */ 1128 @Override 1129 public String[] getStringArray(final String key) { 1130 final String[] result = (String[]) getArray(String.class, key); 1131 return result == null ? ArrayUtils.EMPTY_STRING_ARRAY : result; 1132 } 1133 1134 /** 1135 * Gets the object responsible for synchronizing this configuration. All access to this configuration - both read and 1136 * write access - is controlled by this object. This implementation never returns <strong>null</strong>. If no 1137 * {@code Synchronizer} has been set, a {@link NoOpSynchronizer} is returned. So, per default, instances of 1138 * {@code AbstractConfiguration} are not thread-safe unless a suitable {@code Synchronizer} is set! 1139 * 1140 * @return the {@code Synchronizer} used by this instance 1141 * @since 2.0 1142 */ 1143 @Override 1144 public final Synchronizer getSynchronizer() { 1145 return synchronizer; 1146 } 1147 1148 @Override 1149 public ImmutableConfiguration immutableSubset(final String prefix) { 1150 return ConfigurationUtils.unmodifiableConfiguration(subset(prefix)); 1151 } 1152 1153 /** 1154 * Initializes the logger. Supports <strong>null</strong> input. This method can be called by derived classes in order to enable 1155 * logging. 1156 * 1157 * @param log the logger 1158 * @since 2.0 1159 */ 1160 protected final void initLogger(final ConfigurationLogger log) { 1161 this.log = log != null ? log : ConfigurationLogger.newDummyLogger(); 1162 } 1163 1164 /** 1165 * Creates a default {@code ConfigurationInterpolator} which is initialized with all default {@code Lookup} objects. 1166 * This method is called by the constructor. It ensures that default interpolation works for every new configuration 1167 * instance. 1168 */ 1169 private void installDefaultInterpolator() { 1170 installInterpolator(ConfigurationInterpolator.getDefaultPrefixLookups(), null); 1171 } 1172 1173 /** 1174 * {@inheritDoc} This implementation creates a new {@code ConfigurationInterpolator} instance and initializes it with 1175 * the given {@code Lookup} objects. In addition, it adds a specialized default {@code Lookup} object which queries this 1176 * {@code Configuration}. 1177 * 1178 * @since 2.0 1179 */ 1180 @Override 1181 public final void installInterpolator(final Map<String, ? extends Lookup> prefixLookups, final Collection<? extends Lookup> defLookups) { 1182 final InterpolatorSpecification spec = new InterpolatorSpecification.Builder().withPrefixLookups(prefixLookups).withDefaultLookups(defLookups) 1183 .withDefaultLookup(new ConfigurationLookup(this)).create(); 1184 setInterpolator(ConfigurationInterpolator.fromSpecification(spec)); 1185 } 1186 1187 /** 1188 * Returns the interpolated value. This implementation delegates to the current {@code ConfigurationInterpolator}. If no 1189 * {@code ConfigurationInterpolator} is set, the passed in value is returned without changes. 1190 * 1191 * @param value the value to interpolate 1192 * @return the value with variables substituted 1193 */ 1194 protected Object interpolate(final Object value) { 1195 final ConfigurationInterpolator ci = getInterpolator(); 1196 return ci != null ? ci.interpolate(value) : value; 1197 } 1198 1199 /** 1200 * interpolate key names to handle ${key} stuff 1201 * 1202 * @param base string to interpolate 1203 * @return the key name with the ${key} substituted 1204 */ 1205 protected String interpolate(final String base) { 1206 return Objects.toString(interpolate((Object) base), null); 1207 } 1208 1209 /** 1210 * Returns a configuration with the same content as this configuration, but with all variables replaced by their actual 1211 * values. This method tries to clone the configuration and then perform interpolation on all properties. So property 1212 * values of the form {@code ${var}} will be resolved as far as possible (if a variable cannot be resolved, it remains 1213 * unchanged). This operation is useful if the content of a configuration is to be exported or processed by an external 1214 * component that does not support variable interpolation. 1215 * 1216 * @return a configuration with all variables interpolated 1217 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if this configuration cannot be cloned 1218 * @since 1.5 1219 */ 1220 public Configuration interpolatedConfiguration() { 1221 // first clone this configuration 1222 final AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils.cloneConfiguration(this); 1223 1224 // now perform interpolation 1225 c.setListDelimiterHandler(new DisabledListDelimiterHandler()); 1226 getKeys().forEachRemaining(key -> c.setProperty(key, getList(key))); 1227 c.setListDelimiterHandler(getListDelimiterHandler()); 1228 return c; 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 * Allows setting 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 }