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