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; 033 034import org.apache.commons.configuration2.convert.ConversionHandler; 035import org.apache.commons.configuration2.convert.DefaultConversionHandler; 036import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; 037import org.apache.commons.configuration2.convert.ListDelimiterHandler; 038import org.apache.commons.configuration2.event.BaseEventSource; 039import org.apache.commons.configuration2.event.ConfigurationErrorEvent; 040import org.apache.commons.configuration2.event.ConfigurationEvent; 041import org.apache.commons.configuration2.event.EventListener; 042import org.apache.commons.configuration2.ex.ConversionException; 043import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; 044import org.apache.commons.configuration2.interpol.InterpolatorSpecification; 045import org.apache.commons.configuration2.interpol.Lookup; 046import org.apache.commons.configuration2.io.ConfigurationLogger; 047import org.apache.commons.configuration2.sync.LockMode; 048import org.apache.commons.configuration2.sync.NoOpSynchronizer; 049import org.apache.commons.configuration2.sync.Synchronizer; 050import org.apache.commons.lang3.ArrayUtils; 051import org.apache.commons.lang3.ClassUtils; 052import org.apache.commons.lang3.ObjectUtils; 053 054/** 055 * <p> 056 * Abstract configuration class. Provides basic functionality but does not store any data. 057 * </p> 058 * <p> 059 * If you want to write your own Configuration class then you should implement only abstract methods from this class. A 060 * lot of functionality needed by typical implementations of the {@code Configuration} interface is already provided by 061 * this base class. Following is a list of features implemented here: 062 * </p> 063 * <ul> 064 * <li>Data conversion support. The various data types required by the {@code Configuration} interface are already 065 * handled by this base class. A concrete sub class only needs to provide a generic {@code getProperty()} method.</li> 066 * <li>Support for variable interpolation. Property values containing special variable tokens (like {@code ${var}}) will 067 * be replaced by their corresponding values.</li> 068 * <li>Optional support for string lists. The values of properties to be added to this configuration are checked whether 069 * they contain a list delimiter character. If this is the case and if list splitting is enabled, the string is split 070 * and multiple values are added for this property. List splitting is controlled by a {@link ListDelimiterHandler} 071 * object which can be set using the {@link #setListDelimiterHandler(ListDelimiterHandler)} method. It is disabled per 072 * default. To enable this feature, set a suitable {@code ListDelimiterHandler}, e.g. an instance of 073 * {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler DefaultListDelimiterHandler} configured 074 * with the desired list delimiter character.</li> 075 * <li>Allows specifying how missing properties are treated. Per default the get methods returning an object will return 076 * <b>null</b> if the searched property key is not found (and no default value is provided). With the 077 * {@code setThrowExceptionOnMissing()} method this behavior can be changed to throw an exception when a requested 078 * property cannot be found.</li> 079 * <li>Basic event support. Whenever this configuration is modified registered event listeners are notified. Refer to 080 * the various {@code EVENT_XXX} constants to get an impression about which event types are supported.</li> 081 * <li>Support for proper synchronization based on the {@link Synchronizer} interface.</li> 082 * </ul> 083 * <p> 084 * Most methods defined by the {@code Configuration} interface are already implemented in this class. Many method 085 * implementations perform basic book-keeping tasks (e.g. firing events, handling synchronization), and then delegate to 086 * other (protected) methods executing the actual work. Subclasses override these protected methods to define or adapt 087 * behavior. The public entry point methods are final to prevent subclasses from breaking basic functionality. 088 * </p> 089 * 090 */ 091public abstract class AbstractConfiguration extends BaseEventSource implements Configuration { 092 /** The list delimiter handler. */ 093 private ListDelimiterHandler listDelimiterHandler; 094 095 /** The conversion handler. */ 096 private ConversionHandler conversionHandler; 097 098 /** 099 * Whether the configuration should throw NoSuchElementExceptions or simply return null when a property does not exist. 100 * Defaults to return null. 101 */ 102 private boolean throwExceptionOnMissing; 103 104 /** Stores a reference to the object that handles variable interpolation. */ 105 private AtomicReference<ConfigurationInterpolator> interpolator; 106 107 /** The object responsible for synchronization. */ 108 private volatile Synchronizer synchronizer; 109 110 /** The object used for dealing with encoded property values. */ 111 private ConfigurationDecoder configurationDecoder; 112 113 /** Stores the logger. */ 114 private ConfigurationLogger log; 115 116 /** 117 * Creates a new instance of {@code AbstractConfiguration}. 118 */ 119 public AbstractConfiguration() { 120 interpolator = new AtomicReference<>(); 121 initLogger(null); 122 installDefaultInterpolator(); 123 listDelimiterHandler = DisabledListDelimiterHandler.INSTANCE; 124 conversionHandler = DefaultConversionHandler.INSTANCE; 125 } 126 127 /** 128 * Returns the {@code ListDelimiterHandler} used by this instance. 129 * 130 * @return the {@code ListDelimiterHandler} 131 * @since 2.0 132 */ 133 public ListDelimiterHandler getListDelimiterHandler() { 134 return listDelimiterHandler; 135 } 136 137 /** 138 * <p> 139 * Sets the {@code ListDelimiterHandler} to be used by this instance. This object is invoked every time when dealing 140 * with string properties that may contain a list delimiter and thus have to be split to multiple values. Per default, a 141 * {@code ListDelimiterHandler} implementation is set which does not support list splitting. This can be changed for 142 * instance by setting a {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler 143 * DefaultListDelimiterHandler} object. 144 * </p> 145 * <p> 146 * <strong>Warning:</strong> Be careful when changing the list delimiter handler when the configuration has already been 147 * loaded/populated. List handling is typically applied already when properties are added to the configuration. If later 148 * another handler is set which processes lists differently, results may be unexpected; some operations may even cause 149 * exceptions. 150 * </p> 151 * 152 * @param listDelimiterHandler the {@code ListDelimiterHandler} to be used (must not be <b>null</b>) 153 * @throws IllegalArgumentException if the {@code ListDelimiterHandler} is <b>null</b> 154 * @since 2.0 155 */ 156 public void setListDelimiterHandler(final ListDelimiterHandler listDelimiterHandler) { 157 if (listDelimiterHandler == null) { 158 throw new IllegalArgumentException("List delimiter handler must not be null!"); 159 } 160 this.listDelimiterHandler = listDelimiterHandler; 161 } 162 163 /** 164 * Returns the {@code ConversionHandler} used by this instance. 165 * 166 * @return the {@code ConversionHandler} 167 * @since 2.0 168 */ 169 public ConversionHandler getConversionHandler() { 170 return conversionHandler; 171 } 172 173 /** 174 * Sets the {@code ConversionHandler} to be used by this instance. The {@code ConversionHandler} is responsible for 175 * every kind of data type conversion. It is consulted by all get methods returning results in specific data types. A 176 * newly created configuration uses a default {@code ConversionHandler} implementation. This can be changed while 177 * initializing the configuration (e.g. via a builder). Note that access to this property is not synchronized. 178 * 179 * @param conversionHandler the {@code ConversionHandler} to be used (must not be <b>null</b>) 180 * @throws IllegalArgumentException if the {@code ConversionHandler} is <b>null</b> 181 * @since 2.0 182 */ 183 public void setConversionHandler(final ConversionHandler conversionHandler) { 184 if (conversionHandler == null) { 185 throw new IllegalArgumentException("ConversionHandler must not be null!"); 186 } 187 this.conversionHandler = conversionHandler; 188 } 189 190 /** 191 * Allows to set the {@code throwExceptionOnMissing} flag. This flag controls the behavior of property getter methods 192 * that return objects if the requested property is missing. If the flag is set to <b>false</b> (which is the default 193 * value), these methods will return <b>null</b>. If set to <b>true</b>, they will throw a 194 * {@code NoSuchElementException} exception. Note that getter methods for primitive data types are not affected by this 195 * flag. 196 * 197 * @param throwExceptionOnMissing The new value for the property 198 */ 199 public void setThrowExceptionOnMissing(final boolean throwExceptionOnMissing) { 200 this.throwExceptionOnMissing = throwExceptionOnMissing; 201 } 202 203 /** 204 * Returns true if missing values throw Exceptions. 205 * 206 * @return true if missing values throw Exceptions 207 */ 208 public boolean isThrowExceptionOnMissing() { 209 return throwExceptionOnMissing; 210 } 211 212 /** 213 * Returns the {@code ConfigurationInterpolator} object that manages the lookup objects for resolving variables. 214 * Unless a custom interpolator has been set or the instance has been modified, the returned interpolator will 215 * resolve values from this configuration instance and support the 216 * {@link ConfigurationInterpolator#getDefaultPrefixLookups() default prefix lookups}. 217 * 218 * @return the {@code ConfigurationInterpolator} associated with this configuration 219 * @since 1.4 220 * @see ConfigurationInterpolator#getDefaultPrefixLookups() 221 */ 222 @Override 223 public ConfigurationInterpolator getInterpolator() { 224 return interpolator.get(); 225 } 226 227 /** 228 * {@inheritDoc} This implementation sets the passed in object without further modifications. A <b>null</b> argument is 229 * allowed; this disables interpolation. 230 * 231 * @since 2.0 232 */ 233 @Override 234 public final void setInterpolator(final ConfigurationInterpolator ci) { 235 interpolator.set(ci); 236 } 237 238 /** 239 * {@inheritDoc} This implementation creates a new {@code ConfigurationInterpolator} instance and initializes it with 240 * the given {@code Lookup} objects. In addition, it adds a specialized default {@code Lookup} object which queries this 241 * {@code Configuration}. 242 * 243 * @since 2.0 244 */ 245 @Override 246 public final void installInterpolator(final Map<String, ? extends Lookup> prefixLookups, final Collection<? extends Lookup> defLookups) { 247 final InterpolatorSpecification spec = new InterpolatorSpecification.Builder().withPrefixLookups(prefixLookups).withDefaultLookups(defLookups) 248 .withDefaultLookup(new ConfigurationLookup(this)).create(); 249 setInterpolator(ConfigurationInterpolator.fromSpecification(spec)); 250 } 251 252 /** 253 * Registers all {@code Lookup} objects in the given map at the current {@code ConfigurationInterpolator} of this 254 * configuration. The set of default lookup objects (for variables without a prefix) is not modified by this method. If 255 * this configuration does not have a {@code ConfigurationInterpolator}, a new instance is created. Note: This method is 256 * mainly intended to be used for initializing a configuration when it is created by a builder. Normal client code 257 * should better call {@link #installInterpolator(Map, Collection)} to define the {@code ConfigurationInterpolator} in a 258 * single step. 259 * 260 * @param lookups a map with new {@code Lookup} objects and their prefixes (may be <b>null</b>) 261 * @since 2.0 262 */ 263 public void setPrefixLookups(final Map<String, ? extends Lookup> lookups) { 264 boolean success; 265 do { 266 // do this in a loop because the ConfigurationInterpolator 267 // instance may be changed by another thread 268 final ConfigurationInterpolator ciOld = getInterpolator(); 269 final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator(); 270 ciNew.registerLookups(lookups); 271 success = interpolator.compareAndSet(ciOld, ciNew); 272 } while (!success); 273 } 274 275 /** 276 * Adds all {@code Lookup} objects in the given collection as default lookups (i.e. lookups without a variable prefix) 277 * to the {@code ConfigurationInterpolator} object of this configuration. In addition, it adds a specialized default 278 * {@code Lookup} object which queries this {@code Configuration}. The set of {@code Lookup} objects with prefixes is 279 * not modified by this method. If this configuration does not have a {@code ConfigurationInterpolator}, a new instance 280 * is created. Note: This method is mainly intended to be used for initializing a configuration when it is created by a 281 * builder. Normal client code should better call {@link #installInterpolator(Map, Collection)} to define the 282 * {@code ConfigurationInterpolator} in a single step. 283 * 284 * @param lookups the collection with default {@code Lookup} objects to be added 285 * @since 2.0 286 */ 287 public void setDefaultLookups(final Collection<? extends Lookup> lookups) { 288 boolean success; 289 do { 290 final ConfigurationInterpolator ciOld = getInterpolator(); 291 final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator(); 292 Lookup confLookup = findConfigurationLookup(ciNew); 293 if (confLookup == null) { 294 confLookup = new ConfigurationLookup(this); 295 } else { 296 ciNew.removeDefaultLookup(confLookup); 297 } 298 ciNew.addDefaultLookups(lookups); 299 ciNew.addDefaultLookup(confLookup); 300 success = interpolator.compareAndSet(ciOld, ciNew); 301 } while (!success); 302 } 303 304 /** 305 * Sets the specified {@code ConfigurationInterpolator} as the parent of this configuration's 306 * {@code ConfigurationInterpolator}. If this configuration does not have a {@code ConfigurationInterpolator}, a new 307 * instance is created. Note: This method is mainly intended to be used for initializing a configuration when it is 308 * created by a builder. Normal client code can directly update the {@code ConfigurationInterpolator}. 309 * 310 * @param parent the parent {@code ConfigurationInterpolator} to be set 311 * @since 2.0 312 */ 313 public void setParentInterpolator(final ConfigurationInterpolator parent) { 314 boolean success; 315 do { 316 final ConfigurationInterpolator ciOld = getInterpolator(); 317 final ConfigurationInterpolator ciNew = ciOld != null ? ciOld : new ConfigurationInterpolator(); 318 ciNew.setParentInterpolator(parent); 319 success = interpolator.compareAndSet(ciOld, ciNew); 320 } while (!success); 321 } 322 323 /** 324 * Sets the {@code ConfigurationDecoder} for this configuration. This object is used by 325 * {@link #getEncodedString(String)}. 326 * 327 * @param configurationDecoder the {@code ConfigurationDecoder} 328 * @since 2.0 329 */ 330 public void setConfigurationDecoder(final ConfigurationDecoder configurationDecoder) { 331 this.configurationDecoder = configurationDecoder; 332 } 333 334 /** 335 * Returns the {@code ConfigurationDecoder} used by this instance. 336 * 337 * @return the {@code ConfigurationDecoder} 338 * @since 2.0 339 */ 340 public ConfigurationDecoder getConfigurationDecoder() { 341 return configurationDecoder; 342 } 343 344 /** 345 * Creates a clone of the {@code ConfigurationInterpolator} used by this instance. This method can be called by 346 * {@code clone()} implementations of derived classes. Normally, the {@code ConfigurationInterpolator} of a 347 * configuration instance must not be shared with other instances because it contains a specific {@code Lookup} object 348 * pointing to the owning configuration. This has to be taken into account when cloning a configuration. This method 349 * creates a new {@code ConfigurationInterpolator} for this configuration instance which contains all lookup objects 350 * from the original {@code ConfigurationInterpolator} except for the configuration specific lookup pointing to the 351 * passed in original configuration. This one is replaced by a corresponding {@code Lookup} referring to this 352 * configuration. 353 * 354 * @param orgConfig the original configuration from which this one was cloned 355 * @since 2.0 356 */ 357 protected void cloneInterpolator(final AbstractConfiguration orgConfig) { 358 interpolator = new AtomicReference<>(); 359 final ConfigurationInterpolator orgInterpolator = orgConfig.getInterpolator(); 360 final List<Lookup> defaultLookups = orgInterpolator.getDefaultLookups(); 361 final Lookup lookup = findConfigurationLookup(orgInterpolator, orgConfig); 362 if (lookup != null) { 363 defaultLookups.remove(lookup); 364 } 365 366 installInterpolator(orgInterpolator.getLookups(), defaultLookups); 367 } 368 369 /** 370 * Creates a default {@code ConfigurationInterpolator} which is initialized with all default {@code Lookup} objects. 371 * This method is called by the constructor. It ensures that default interpolation works for every new configuration 372 * instance. 373 */ 374 private void installDefaultInterpolator() { 375 installInterpolator(ConfigurationInterpolator.getDefaultPrefixLookups(), null); 376 } 377 378 /** 379 * Finds a {@code ConfigurationLookup} pointing to this configuration in the default lookups of the specified 380 * {@code ConfigurationInterpolator}. This method is called to ensure that there is exactly one default lookup querying 381 * this configuration. 382 * 383 * @param ci the {@code ConfigurationInterpolator} in question 384 * @return the found {@code Lookup} object or <b>null</b> 385 */ 386 private Lookup findConfigurationLookup(final ConfigurationInterpolator ci) { 387 return findConfigurationLookup(ci, this); 388 } 389 390 /** 391 * Finds a {@code ConfigurationLookup} pointing to the specified configuration in the default lookups for the specified 392 * {@code ConfigurationInterpolator}. 393 * 394 * @param ci the {@code ConfigurationInterpolator} in question 395 * @param targetConf the target configuration of the searched lookup 396 * @return the found {@code Lookup} object or <b>null</b> 397 */ 398 private static Lookup findConfigurationLookup(final ConfigurationInterpolator ci, final ImmutableConfiguration targetConf) { 399 for (final Lookup l : ci.getDefaultLookups()) { 400 if (l instanceof ConfigurationLookup && targetConf == ((ConfigurationLookup) l).getConfiguration()) { 401 return l; 402 } 403 } 404 return null; 405 } 406 407 /** 408 * Returns the logger used by this configuration object. 409 * 410 * @return the logger 411 * @since 2.0 412 */ 413 public ConfigurationLogger getLogger() { 414 return log; 415 } 416 417 /** 418 * Allows setting the logger to be used by this configuration object. This method makes it possible for clients to 419 * exactly control logging behavior. Per default a logger is set that will ignore all log messages. Derived classes that 420 * want to enable logging should call this method during their initialization with the logger to be used. It is legal to 421 * pass a <b>null</b> logger; in this case, logging will be disabled. 422 * 423 * @param log the new logger 424 * @since 2.0 425 */ 426 public void setLogger(final ConfigurationLogger log) { 427 initLogger(log); 428 } 429 430 /** 431 * Adds a special {@link EventListener} object to this configuration that will log all internal errors. This method is 432 * intended to be used by certain derived classes, for which it is known that they can fail on property access (e.g. 433 * {@code DatabaseConfiguration}). 434 * 435 * @since 1.4 436 */ 437 public final void addErrorLogListener() { 438 addEventListener(ConfigurationErrorEvent.ANY, event -> getLogger().warn("Internal error", event.getCause())); 439 } 440 441 /** 442 * Returns the object responsible for synchronizing this configuration. All access to this configuration - both read and 443 * write access - is controlled by this object. This implementation never returns <b>null</b>. If no 444 * {@code Synchronizer} has been set, a {@link NoOpSynchronizer} is returned. So, per default, instances of 445 * {@code AbstractConfiguration} are not thread-safe unless a suitable {@code Synchronizer} is set! 446 * 447 * @return the {@code Synchronizer} used by this instance 448 * @since 2.0 449 */ 450 @Override 451 public final Synchronizer getSynchronizer() { 452 final Synchronizer sync = synchronizer; 453 return sync != null ? sync : NoOpSynchronizer.INSTANCE; 454 } 455 456 /** 457 * Sets the object responsible for synchronizing this configuration. This method has to be called with a suitable 458 * {@code Synchronizer} object when initializing this configuration instance in order to make it thread-safe. 459 * 460 * @param synchronizer the new {@code Synchronizer}; can be <b>null</b>, then this instance uses a 461 * {@link NoOpSynchronizer} 462 * @since 2.0 463 */ 464 @Override 465 public final void setSynchronizer(final Synchronizer synchronizer) { 466 this.synchronizer = synchronizer; 467 } 468 469 /** 470 * {@inheritDoc} This implementation delegates to {@code beginRead()} or {@code beginWrite()}, depending on the 471 * {@code LockMode} argument. Subclasses can override these protected methods to perform additional steps when a 472 * configuration is locked. 473 * 474 * @since 2.0 475 * @throws NullPointerException if the argument is <b>null</b> 476 */ 477 @Override 478 public final void lock(final LockMode mode) { 479 switch (mode) { 480 case READ: 481 beginRead(false); 482 break; 483 case WRITE: 484 beginWrite(false); 485 break; 486 default: 487 throw new IllegalArgumentException("Unsupported LockMode: " + mode); 488 } 489 } 490 491 /** 492 * {@inheritDoc} This implementation delegates to {@code endRead()} or {@code endWrite()}, depending on the 493 * {@code LockMode} argument. Subclasses can override these protected methods to perform additional steps when a 494 * configuration's lock is released. 495 * 496 * @throws NullPointerException if the argument is <b>null</b> 497 */ 498 @Override 499 public final void unlock(final LockMode mode) { 500 switch (mode) { 501 case READ: 502 endRead(); 503 break; 504 case WRITE: 505 endWrite(); 506 break; 507 default: 508 throw new IllegalArgumentException("Unsupported LockMode: " + mode); 509 } 510 } 511 512 /** 513 * Notifies this configuration's {@link Synchronizer} that a read operation is about to start. This method is called by 514 * all methods which access this configuration in a read-only mode. Subclasses may override it to perform additional 515 * actions before this read operation. The boolean <em>optimize</em> argument can be evaluated by overridden methods in 516 * derived classes. Some operations which require a lock do not need a fully initialized configuration object. By 517 * setting this flag to <strong>true</strong>, such operations can give a corresponding hint. An overridden 518 * implementation of {@code beginRead()} can then decide to skip some initialization steps. All basic operations in this 519 * class (and most of the basic {@code Configuration} implementations) call this method with a parameter value of 520 * <strong>false</strong>. <strong>In any case the inherited method must be called! Otherwise, proper synchronization is 521 * not guaranteed.</strong> 522 * 523 * @param optimize a flag whether optimization can be performed 524 * @since 2.0 525 */ 526 protected void beginRead(final boolean optimize) { 527 getSynchronizer().beginRead(); 528 } 529 530 /** 531 * Notifies this configuration's {@link Synchronizer} that a read operation has finished. This method is called by all 532 * methods which access this configuration in a read-only manner at the end of their execution. Subclasses may override 533 * it to perform additional actions after this read operation. <strong>In any case the inherited method must be called! 534 * Otherwise, the read lock will not be released.</strong> 535 * 536 * @since 2.0 537 */ 538 protected void endRead() { 539 getSynchronizer().endRead(); 540 } 541 542 /** 543 * Notifies this configuration's {@link Synchronizer} that an update operation is about to start. This method is called 544 * by all methods which modify this configuration. Subclasses may override it to perform additional operations before an 545 * update. For a description of the boolean <em>optimize</em> argument refer to the documentation of 546 * {@code beginRead()}. <strong>In any case the inherited method must be called! Otherwise, proper synchronization is 547 * not guaranteed.</strong> 548 * 549 * @param optimize a flag whether optimization can be performed 550 * @see #beginRead(boolean) 551 * @since 2.0 552 */ 553 protected void beginWrite(final boolean optimize) { 554 getSynchronizer().beginWrite(); 555 } 556 557 /** 558 * Notifies this configuration's {@link Synchronizer} that an update operation has finished. This method is called by 559 * all methods which modify this configuration at the end of their execution. Subclasses may override it to perform 560 * additional operations after an update. <strong>In any case the inherited method must be called! Otherwise, the write 561 * lock will not be released.</strong> 562 * 563 * @since 2.0 564 */ 565 protected void endWrite() { 566 getSynchronizer().endWrite(); 567 } 568 569 @Override 570 public final void addProperty(final String key, final Object value) { 571 beginWrite(false); 572 try { 573 fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, true); 574 addPropertyInternal(key, value); 575 fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, false); 576 } finally { 577 endWrite(); 578 } 579 } 580 581 /** 582 * Actually adds a property to this configuration. This method is called by {@code addProperty()}. It performs list 583 * splitting if necessary and delegates to {@link #addPropertyDirect(String, Object)} for every single property value. 584 * 585 * @param key the key of the property to be added 586 * @param value the new property value 587 * @since 2.0 588 */ 589 protected void addPropertyInternal(final String key, final Object value) { 590 for (final Object obj : getListDelimiterHandler().parse(value)) { 591 addPropertyDirect(key, obj); 592 } 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 * Returns 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 && "".equals(token)) { 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 for (final Object elem : l) { 1187 list.add(interpolate(elem)); 1188 } 1189 } else if (value == null) { 1190 // This is okay because we just return this list to the caller 1191 @SuppressWarnings("unchecked") 1192 final List<Object> resultList = (List<Object>) defaultValue; 1193 list = resultList; 1194 } else if (value.getClass().isArray()) { 1195 return Arrays.asList((Object[]) value); 1196 } else if (isScalarValue(value)) { 1197 return Collections.singletonList((Object) value.toString()); 1198 } else { 1199 throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " + value.getClass().getName()); 1200 } 1201 return list; 1202 } 1203 1204 @Override 1205 public <T> T get(final Class<T> cls, final String key) { 1206 return convert(cls, key, null, true); 1207 } 1208 1209 /** 1210 * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion. 1211 */ 1212 @Override 1213 public <T> T get(final Class<T> cls, final String key, final T defaultValue) { 1214 return convert(cls, key, defaultValue, false); 1215 } 1216 1217 @Override 1218 public Object getArray(final Class<?> cls, final String key) { 1219 return getArray(cls, key, null); 1220 } 1221 1222 /** 1223 * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual type conversion. 1224 * If this results in a <b>null</b> result (because the property is undefined), the default value is returned. It is 1225 * checked whether the default value is an array with the correct component type. If not, an exception is thrown. 1226 * 1227 * @throws IllegalArgumentException if the default value is not a compatible array 1228 */ 1229 @Override 1230 public Object getArray(final Class<?> cls, final String key, final Object defaultValue) { 1231 return convertToArray(cls, key, defaultValue); 1232 } 1233 1234 @Override 1235 public <T> List<T> getList(final Class<T> cls, final String key) { 1236 return getList(cls, key, null); 1237 } 1238 1239 /** 1240 * {@inheritDoc} This implementation delegates to the generic {@code getCollection()}. As target collection a newly 1241 * created {@code ArrayList} is passed in. 1242 */ 1243 @Override 1244 public <T> List<T> getList(final Class<T> cls, final String key, final List<T> defaultValue) { 1245 final List<T> result = new ArrayList<>(); 1246 if (getCollection(cls, key, result, defaultValue) == null) { 1247 return null; 1248 } 1249 return result; 1250 } 1251 1252 @Override 1253 public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target) { 1254 return getCollection(cls, key, target, null); 1255 } 1256 1257 /** 1258 * {@inheritDoc} This implementation delegates to the {@link ConversionHandler} to perform the actual conversion. If no 1259 * target collection is provided, an {@code ArrayList} is created. 1260 */ 1261 @Override 1262 public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target, final Collection<T> defaultValue) { 1263 final Object src = getProperty(key); 1264 if (src == null) { 1265 return handleDefaultCollection(target, defaultValue); 1266 } 1267 1268 final Collection<T> targetCol = target != null ? target : new ArrayList<>(); 1269 getConversionHandler().toCollection(src, cls, getInterpolator(), targetCol); 1270 return targetCol; 1271 } 1272 1273 /** 1274 * Checks whether the specified object is a scalar value. This method is called by {@code getList()} and 1275 * {@code getStringArray()} if the property requested is not a string, a list, or an array. If it returns <b>true</b>, 1276 * the calling method transforms the value to a string and returns a list or an array with this single element. This 1277 * implementation returns <b>true</b> if the value is of a wrapper type for a primitive type. 1278 * 1279 * @param value the value to be checked 1280 * @return a flag whether the value is a scalar 1281 * @since 1.7 1282 */ 1283 protected boolean isScalarValue(final Object value) { 1284 return ClassUtils.wrapperToPrimitive(value.getClass()) != null; 1285 } 1286 1287 /** 1288 * Copies the content of the specified configuration into this configuration. If the specified configuration contains a 1289 * key that is also present in this configuration, the value of this key will be replaced by the new value. 1290 * <em>Note:</em> This method won't work well when copying hierarchical configurations because it is not able to copy 1291 * information about the properties' structure (i.e. the parent-child-relationships will get lost). So when dealing with 1292 * hierarchical configuration objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be 1293 * used. 1294 * 1295 * @param c the configuration to copy (can be <b>null</b>, then this operation will have no effect) 1296 * @since 1.5 1297 */ 1298 public void copy(final Configuration c) { 1299 if (c != null) { 1300 c.lock(LockMode.READ); 1301 try { 1302 for (final Iterator<String> it = c.getKeys(); it.hasNext();) { 1303 final String key = it.next(); 1304 final Object value = encodeForCopy(c.getProperty(key)); 1305 setProperty(key, value); 1306 } 1307 } finally { 1308 c.unlock(LockMode.READ); 1309 } 1310 } 1311 } 1312 1313 /** 1314 * Appends the content of the specified configuration to this configuration. The values of all properties contained in 1315 * the specified configuration will be appended to this configuration. So if a property is already present in this 1316 * configuration, its new value will be a union of the values in both configurations. <em>Note:</em> This method won't 1317 * work well when appending hierarchical configurations because it is not able to copy information about the properties' 1318 * structure (i.e. the parent-child-relationships will get lost). So when dealing with hierarchical configuration 1319 * objects their {@link BaseHierarchicalConfiguration#clone() clone()} methods should be used. 1320 * 1321 * @param c the configuration to be appended (can be <b>null</b>, then this operation will have no effect) 1322 * @since 1.5 1323 */ 1324 public void append(final Configuration c) { 1325 if (c != null) { 1326 c.lock(LockMode.READ); 1327 try { 1328 for (final Iterator<String> it = c.getKeys(); it.hasNext();) { 1329 final String key = it.next(); 1330 final Object value = encodeForCopy(c.getProperty(key)); 1331 addProperty(key, value); 1332 } 1333 } finally { 1334 c.unlock(LockMode.READ); 1335 } 1336 } 1337 } 1338 1339 /** 1340 * Returns a configuration with the same content as this configuration, but with all variables replaced by their actual 1341 * values. This method tries to clone the configuration and then perform interpolation on all properties. So property 1342 * values of the form {@code ${var}} will be resolved as far as possible (if a variable cannot be resolved, it remains 1343 * unchanged). This operation is useful if the content of a configuration is to be exported or processed by an external 1344 * component that does not support variable interpolation. 1345 * 1346 * @return a configuration with all variables interpolated 1347 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if this configuration cannot be cloned 1348 * @since 1.5 1349 */ 1350 public Configuration interpolatedConfiguration() { 1351 // first clone this configuration 1352 final AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils.cloneConfiguration(this); 1353 1354 // now perform interpolation 1355 c.setListDelimiterHandler(new DisabledListDelimiterHandler()); 1356 for (final Iterator<String> it = getKeys(); it.hasNext();) { 1357 final String key = it.next(); 1358 c.setProperty(key, getList(key)); 1359 } 1360 1361 c.setListDelimiterHandler(getListDelimiterHandler()); 1362 return c; 1363 } 1364 1365 /** 1366 * Initializes the logger. Supports <b>null</b> input. This method can be called by derived classes in order to enable 1367 * logging. 1368 * 1369 * @param log the logger 1370 * @since 2.0 1371 */ 1372 protected final void initLogger(final ConfigurationLogger log) { 1373 this.log = log != null ? log : ConfigurationLogger.newDummyLogger(); 1374 } 1375 1376 /** 1377 * Encodes a property value so that it can be added to this configuration. This method deals with list delimiters. The 1378 * passed in object has to be escaped so that an add operation yields the same result. If it is a list, all of its 1379 * values have to be escaped. 1380 * 1381 * @param value the value to be encoded 1382 * @return the encoded value 1383 */ 1384 private Object encodeForCopy(final Object value) { 1385 if (value instanceof Collection) { 1386 return encodeListForCopy((Collection<?>) value); 1387 } 1388 return getListDelimiterHandler().escape(value, ListDelimiterHandler.NOOP_TRANSFORMER); 1389 } 1390 1391 /** 1392 * Encodes a list with property values so that it can be added to this configuration. This method calls 1393 * {@code encodeForCopy()} for all list elements. 1394 * 1395 * @param values the list to be encoded 1396 * @return a list with encoded elements 1397 */ 1398 private Object encodeListForCopy(final Collection<?> values) { 1399 final List<Object> result = new ArrayList<>(values.size()); 1400 for (final Object value : values) { 1401 result.add(encodeForCopy(value)); 1402 } 1403 return result; 1404 } 1405 1406 /** 1407 * Obtains the property value for the specified key and converts it to the given target class. 1408 * 1409 * @param <T> the target type of the conversion 1410 * @param cls the target class 1411 * @param key the key of the desired property 1412 * @param defaultValue a default value 1413 * @return the converted value of this property 1414 * @throws ConversionException if the conversion cannot be performed 1415 */ 1416 private <T> T getAndConvertProperty(final Class<T> cls, final String key, final T defaultValue) { 1417 final Object value = getProperty(key); 1418 try { 1419 return ObjectUtils.defaultIfNull(getConversionHandler().to(value, cls, getInterpolator()), defaultValue); 1420 } catch (final ConversionException cex) { 1421 // improve error message 1422 throw new ConversionException(String.format("Key '%s' cannot be converted to class %s. Value is: '%s'.", key, cls.getName(), String.valueOf(value)), 1423 cex.getCause()); 1424 } 1425 } 1426 1427 /** 1428 * Helper method for obtaining a property value with a type conversion. 1429 * 1430 * @param <T> the target type of the conversion 1431 * @param cls the target class 1432 * @param key the key of the desired property 1433 * @param defValue a default value 1434 * @param throwOnMissing a flag whether an exception should be thrown for a missing value 1435 * @return the converted value 1436 */ 1437 private <T> T convert(final Class<T> cls, final String key, final T defValue, final boolean throwOnMissing) { 1438 if (cls.isArray()) { 1439 return cls.cast(convertToArray(cls.getComponentType(), key, defValue)); 1440 } 1441 1442 final T result = getAndConvertProperty(cls, key, defValue); 1443 if (result == null) { 1444 if (throwOnMissing && isThrowExceptionOnMissing()) { 1445 throwMissingPropertyException(key); 1446 } 1447 return defValue; 1448 } 1449 1450 return result; 1451 } 1452 1453 /** 1454 * Performs a conversion to an array result class. This implementation delegates to the {@link ConversionHandler} to 1455 * perform the actual type conversion. If this results in a <b>null</b> result (because the property is undefined), the 1456 * default value is returned. It is checked whether the default value is an array with the correct component type. If 1457 * not, an exception is thrown. 1458 * 1459 * @param cls the component class of the array 1460 * @param key the configuration key 1461 * @param defaultValue an optional default value 1462 * @return the converted array 1463 * @throws IllegalArgumentException if the default value is not a compatible array 1464 */ 1465 private Object convertToArray(final Class<?> cls, final String key, final Object defaultValue) { 1466 checkDefaultValueArray(cls, defaultValue); 1467 return ObjectUtils.defaultIfNull(getConversionHandler().toArray(getProperty(key), cls, getInterpolator()), defaultValue); 1468 } 1469 1470 /** 1471 * Checks an object provided as default value for the {@code getArray()} method. Throws an exception if this is not an 1472 * array with the correct component type. 1473 * 1474 * @param cls the component class for the array 1475 * @param defaultValue the default value object to be checked 1476 * @throws IllegalArgumentException if this is not a valid default object 1477 */ 1478 private static void checkDefaultValueArray(final Class<?> cls, final Object defaultValue) { 1479 if (defaultValue != null && (!defaultValue.getClass().isArray() || !cls.isAssignableFrom(defaultValue.getClass().getComponentType()))) { 1480 throw new IllegalArgumentException( 1481 "The type of the default value (" + defaultValue.getClass() + ")" + " is not an array of the specified class (" + cls + ")"); 1482 } 1483 } 1484 1485 /** 1486 * Handles the default collection for a collection conversion. This method fills the target collection with the content 1487 * of the default collection. Both collections may be <b>null</b>. 1488 * 1489 * @param target the target collection 1490 * @param defaultValue the default collection 1491 * @return the initialized target collection 1492 */ 1493 private static <T> Collection<T> handleDefaultCollection(final Collection<T> target, final Collection<T> defaultValue) { 1494 if (defaultValue == null) { 1495 return null; 1496 } 1497 1498 final Collection<T> result; 1499 if (target == null) { 1500 result = new ArrayList<>(defaultValue); 1501 } else { 1502 target.addAll(defaultValue); 1503 result = target; 1504 } 1505 return result; 1506 } 1507 1508 /** 1509 * Checks whether the specified value is <b>null</b> and throws an exception in this case. This method is used by 1510 * conversion methods returning primitive Java types. Here values to be returned must not be <b>null</b>. 1511 * 1512 * @param <T> the type of the object to be checked 1513 * @param key the key which caused the problem 1514 * @param value the value to be checked 1515 * @return the passed in value for chaining this method call 1516 * @throws NoSuchElementException if the value is <b>null</b> 1517 */ 1518 private static <T> T checkNonNullValue(final String key, final T value) { 1519 if (value == null) { 1520 throwMissingPropertyException(key); 1521 } 1522 return value; 1523 } 1524 1525 /** 1526 * Helper method for throwing an exception for a key that does not map to an existing object. 1527 * 1528 * @param key the key (to be part of the error message) 1529 */ 1530 private static void throwMissingPropertyException(final String key) { 1531 throw new NoSuchElementException(String.format("Key '%s' does not map to an existing object!", key)); 1532 } 1533}