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