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