001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.configuration2; 019 020import java.math.BigDecimal; 021import java.math.BigInteger; 022import java.time.Duration; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030import java.util.NoSuchElementException; 031import java.util.Objects; 032import java.util.Properties; 033import java.util.concurrent.atomic.AtomicReference; 034import java.util.stream.Collectors; 035 036import org.apache.commons.configuration2.convert.ConversionHandler; 037import org.apache.commons.configuration2.convert.DefaultConversionHandler; 038import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; 039import org.apache.commons.configuration2.convert.ListDelimiterHandler; 040import org.apache.commons.configuration2.event.BaseEventSource; 041import org.apache.commons.configuration2.event.ConfigurationErrorEvent; 042import org.apache.commons.configuration2.event.ConfigurationEvent; 043import org.apache.commons.configuration2.event.EventListener; 044import org.apache.commons.configuration2.ex.ConversionException; 045import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; 046import org.apache.commons.configuration2.interpol.InterpolatorSpecification; 047import org.apache.commons.configuration2.interpol.Lookup; 048import org.apache.commons.configuration2.io.ConfigurationLogger; 049import org.apache.commons.configuration2.sync.LockMode; 050import org.apache.commons.configuration2.sync.NoOpSynchronizer; 051import org.apache.commons.configuration2.sync.Synchronizer; 052import org.apache.commons.lang3.ArrayUtils; 053import org.apache.commons.lang3.ClassUtils; 054import org.apache.commons.lang3.ObjectUtils; 055import org.apache.commons.lang3.StringUtils; 056import org.apache.commons.lang3.function.FailableRunnable; 057import org.apache.commons.lang3.function.FailableSupplier; 058 059/** 060 * <p> 061 * Abstract configuration class. Provides basic functionality but does not store any data. 062 * </p> 063 * <p> 064 * If you want to write your own Configuration class then you should implement only abstract methods from this class. A 065 * lot of functionality needed by typical implementations of the {@code Configuration} interface is already provided by 066 * this base class. Following is a list of features implemented here: 067 * </p> 068 * <ul> 069 * <li>Data conversion support. The various data types required by the {@code Configuration} interface are already 070 * handled by this base class. A concrete sub class only needs to provide a generic {@code getProperty()} method.</li> 071 * <li>Support for variable interpolation. Property values containing special variable tokens (like {@code ${var}}) will 072 * be replaced by their corresponding values.</li> 073 * <li>Optional support for string lists. The values of properties to be added to this configuration are checked whether 074 * they contain a list delimiter character. If this is the case and if list splitting is enabled, the string is split 075 * and multiple values are added for this property. List splitting is controlled by a {@link ListDelimiterHandler} 076 * object which can be set using the {@link #setListDelimiterHandler(ListDelimiterHandler)} method. It is disabled per 077 * default. To enable this feature, set a suitable {@code ListDelimiterHandler}, for example an instance of 078 * {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler DefaultListDelimiterHandler} configured 079 * with the desired list delimiter character.</li> 080 * <li>Allows specifying how missing properties are treated. Per default the get methods returning an object will return 081 * <strong>null</strong> if the searched property key is not found (and no default value is provided). With the 082 * {@code setThrowExceptionOnMissing()} method this behavior can be changed to throw an exception when a requested 083 * property cannot be found.</li> 084 * <li>Basic event support. Whenever this configuration is modified registered event listeners are notified. Refer to 085 * the various {@code EVENT_XXX} constants to get an impression about which event types are supported.</li> 086 * <li>Support for proper synchronization based on the {@link Synchronizer} interface.</li> 087 * </ul> 088 * <p> 089 * Most methods defined by the {@code Configuration} interface are already implemented in this class. Many method 090 * implementations perform basic book-keeping tasks (for example firing events, handling synchronization), and then delegate to 091 * other (protected) methods executing the actual work. Subclasses override these protected methods to define or adapt 092 * behavior. The public entry point methods are final to prevent subclasses from breaking basic functionality. 093 * </p> 094 */ 095public abstract class AbstractConfiguration extends BaseEventSource implements Configuration { 096 097 /** 098 * Default configuration delimiter for properties and keys. 099 */ 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 * Actually 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}