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 1827667 2018-03-24 19:57:23Z oheger $ 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 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(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(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(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 Map<String, ? extends Lookup> prefixLookups, 287 Collection<? extends Lookup> defLookups) 288 { 289 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(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 ConfigurationInterpolator ciOld = getInterpolator(); 321 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(Collection<? extends Lookup> lookups) 346 { 347 boolean success; 348 do 349 { 350 ConfigurationInterpolator ciOld = getInterpolator(); 351 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(ConfigurationInterpolator parent) 380 { 381 boolean success; 382 do 383 { 384 ConfigurationInterpolator ciOld = getInterpolator(); 385 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 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(AbstractConfiguration orgConfig) 436 { 437 interpolator = new AtomicReference<>(); 438 ConfigurationInterpolator orgInterpolator = orgConfig.getInterpolator(); 439 List<Lookup> defaultLookups = orgInterpolator.getDefaultLookups(); 440 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(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(ConfigurationInterpolator ci, 485 ImmutableConfiguration targetConf) 486 { 487 for (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(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(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 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(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(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(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(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(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(String key, 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(String key, Object value) 729 { 730 for (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(String base) 753 { 754 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(Object value) 768 { 769 ConfigurationInterpolator ci = getInterpolator(); 770 return (ci != null) ? ci.interpolate(value) : value; 771 } 772 773 @Override 774 public Configuration subset(String prefix) 775 { 776 return new SubsetConfiguration(this, prefix, "."); 777 } 778 779 @Override 780 public ImmutableConfiguration immutableSubset(String prefix) 781 { 782 return ConfigurationUtils.unmodifiableConfiguration(subset(prefix)); 783 } 784 785 @Override 786 public final void setProperty(String key, 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(String key, 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(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 Iterator<String> it = getKeys(); 888 while (it.hasNext()) 889 { 890 String key = it.next(); 891 if (useIterator) 892 { 893 try 894 { 895 it.remove(); 896 } 897 catch (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(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(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(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 (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(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(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(String key, Properties defaults) 1138 { 1139 /* 1140 * Grab an array of the tokens for this key. 1141 */ 1142 String[] tokens = getStringArray(key); 1143 1144 /* 1145 * Each token is of the form 'key=value'. 1146 */ 1147 Properties props = defaults == null ? new Properties() : new Properties(defaults); 1148 for (String token : tokens) 1149 { 1150 int equalSign = token.indexOf('='); 1151 if (equalSign > 0) 1152 { 1153 String pkey = token.substring(0, equalSign).trim(); 1154 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(String key) 1173 { 1174 Boolean b = convert(Boolean.class, key, null, true); 1175 return checkNonNullValue(key, b).booleanValue(); 1176 } 1177 1178 @Override 1179 public boolean getBoolean(String key, 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(String key, Boolean defaultValue) 1197 { 1198 return convert(Boolean.class, key, defaultValue, false); 1199 } 1200 1201 @Override 1202 public byte getByte(String key) 1203 { 1204 Byte b = convert(Byte.class, key, null, true); 1205 return checkNonNullValue(key, b).byteValue(); 1206 } 1207 1208 @Override 1209 public byte getByte(String key, byte defaultValue) 1210 { 1211 return getByte(key, Byte.valueOf(defaultValue)).byteValue(); 1212 } 1213 1214 @Override 1215 public Byte getByte(String key, Byte defaultValue) 1216 { 1217 return convert(Byte.class, key, defaultValue, false); 1218 } 1219 1220 @Override 1221 public double getDouble(String key) 1222 { 1223 Double d = convert(Double.class, key, null, true); 1224 return checkNonNullValue(key, d).doubleValue(); 1225 } 1226 1227 @Override 1228 public double getDouble(String key, double defaultValue) 1229 { 1230 return getDouble(key, Double.valueOf(defaultValue)).doubleValue(); 1231 } 1232 1233 @Override 1234 public Double getDouble(String key, Double defaultValue) 1235 { 1236 return convert(Double.class, key, defaultValue, false); 1237 } 1238 1239 @Override 1240 public float getFloat(String key) 1241 { 1242 Float f = convert(Float.class, key, null, true); 1243 return checkNonNullValue(key, f).floatValue(); 1244 } 1245 1246 @Override 1247 public float getFloat(String key, float defaultValue) 1248 { 1249 return getFloat(key, Float.valueOf(defaultValue)).floatValue(); 1250 } 1251 1252 @Override 1253 public Float getFloat(String key, Float defaultValue) 1254 { 1255 return convert(Float.class, key, defaultValue, false); 1256 } 1257 1258 @Override 1259 public int getInt(String key) 1260 { 1261 Integer i = convert(Integer.class, key, null, true); 1262 return checkNonNullValue(key, i).intValue(); 1263 } 1264 1265 @Override 1266 public int getInt(String key, int defaultValue) 1267 { 1268 return getInteger(key, Integer.valueOf(defaultValue)).intValue(); 1269 } 1270 1271 @Override 1272 public Integer getInteger(String key, Integer defaultValue) 1273 { 1274 return convert(Integer.class, key, defaultValue, false); 1275 } 1276 1277 @Override 1278 public long getLong(String key) 1279 { 1280 Long l = convert(Long.class, key, null, true); 1281 return checkNonNullValue(key, l).longValue(); 1282 } 1283 1284 @Override 1285 public long getLong(String key, long defaultValue) 1286 { 1287 return getLong(key, Long.valueOf(defaultValue)).longValue(); 1288 } 1289 1290 @Override 1291 public Long getLong(String key, Long defaultValue) 1292 { 1293 return convert(Long.class, key, defaultValue, false); 1294 } 1295 1296 @Override 1297 public short getShort(String key) 1298 { 1299 Short s = convert(Short.class, key, null, true); 1300 return checkNonNullValue(key, s).shortValue(); 1301 } 1302 1303 @Override 1304 public short getShort(String key, short defaultValue) 1305 { 1306 return getShort(key, Short.valueOf(defaultValue)).shortValue(); 1307 } 1308 1309 @Override 1310 public Short getShort(String key, 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(String key) 1321 { 1322 return convert(BigDecimal.class, key, null, true); 1323 } 1324 1325 @Override 1326 public BigDecimal getBigDecimal(String key, 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(String key) 1337 { 1338 return convert(BigInteger.class, key, null, true); 1339 } 1340 1341 @Override 1342 public BigInteger getBigInteger(String key, 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(String key) 1353 { 1354 return convert(String.class, key, null, true); 1355 } 1356 1357 @Override 1358 public String getString(String key, String defaultValue) 1359 { 1360 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(String key, ConfigurationDecoder decoder) 1373 { 1374 if (decoder == null) 1375 { 1376 throw new IllegalArgumentException( 1377 "ConfigurationDecoder must not be null!"); 1378 } 1379 1380 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(String key) 1395 { 1396 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(String key) 1423 { 1424 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(String key) 1434 { 1435 return getList(key, new ArrayList<>()); 1436 } 1437 1438 @Override 1439 public List<Object> getList(String key, List<?> defaultValue) 1440 { 1441 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 List<?> l = (List<?>) value; 1453 1454 // add the interpolated elements in the new list 1455 for (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 List<Object> resultList = (List<Object>) defaultValue; 1465 list = resultList; 1466 } 1467 else if (value.getClass().isArray()) 1468 { 1469 return Arrays.asList((Object[]) value); 1470 } 1471 else if (isScalarValue(value)) 1472 { 1473 return Collections.singletonList((Object) value.toString()); 1474 } 1475 else 1476 { 1477 throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " 1478 + value.getClass().getName()); 1479 } 1480 return list; 1481 } 1482 1483 @Override 1484 public <T> T get(Class<T> cls, String key) 1485 { 1486 return convert(cls, key, null, true); 1487 } 1488 1489 /** 1490 * {@inheritDoc} This implementation delegates to the 1491 * {@link ConversionHandler} to perform the actual type conversion. 1492 */ 1493 @Override 1494 public <T> T get(Class<T> cls, String key, T defaultValue) 1495 { 1496 return convert(cls, key, defaultValue, false); 1497 } 1498 1499 @Override 1500 public Object getArray(Class<?> cls, String key) 1501 { 1502 return getArray(cls, key, null); 1503 } 1504 1505 /** 1506 * {@inheritDoc} This implementation delegates to the 1507 * {@link ConversionHandler} to perform the actual type conversion. If this 1508 * results in a <b>null</b> result (because the property is undefined), the 1509 * default value is returned. It is checked whether the default value is an 1510 * array with the correct component type. If not, an exception is thrown. 1511 * 1512 * @throws IllegalArgumentException if the default value is not a compatible 1513 * array 1514 */ 1515 @Override 1516 public Object getArray(Class<?> cls, String key, Object defaultValue) 1517 { 1518 return convertToArray(cls, key, defaultValue); 1519 } 1520 1521 @Override 1522 public <T> List<T> getList(Class<T> cls, String key) 1523 { 1524 return getList(cls, key, null); 1525 } 1526 1527 /** 1528 * {@inheritDoc} This implementation delegates to the generic 1529 * {@code getCollection()}. As target collection a newly created 1530 * {@code ArrayList} is passed in. 1531 */ 1532 @Override 1533 public <T> List<T> getList(Class<T> cls, String key, List<T> defaultValue) 1534 { 1535 List<T> result = new ArrayList<>(); 1536 if (getCollection(cls, key, result, defaultValue) == null) 1537 { 1538 return null; 1539 } 1540 return result; 1541 } 1542 1543 @Override 1544 public <T> Collection<T> getCollection(Class<T> cls, String key, 1545 Collection<T> target) 1546 { 1547 return getCollection(cls, key, target, null); 1548 } 1549 1550 /** 1551 * {@inheritDoc} This implementation delegates to the 1552 * {@link ConversionHandler} to perform the actual conversion. If no target 1553 * collection is provided, an {@code ArrayList} is created. 1554 */ 1555 @Override 1556 public <T> Collection<T> getCollection(Class<T> cls, String key, 1557 Collection<T> target, Collection<T> defaultValue) 1558 { 1559 Object src = getProperty(key); 1560 if (src == null) 1561 { 1562 return handleDefaultCollection(target, defaultValue); 1563 } 1564 1565 Collection<T> targetCol = 1566 (target != null) ? target : new ArrayList<T>(); 1567 getConversionHandler().toCollection(src, cls, getInterpolator(), 1568 targetCol); 1569 return targetCol; 1570 } 1571 1572 /** 1573 * Checks whether the specified object is a scalar value. This method is 1574 * called by {@code getList()} and {@code getStringArray()} if the 1575 * property requested is not a string, a list, or an array. If it returns 1576 * <b>true</b>, the calling method transforms the value to a string and 1577 * returns a list or an array with this single element. This implementation 1578 * returns <b>true</b> if the value is of a wrapper type for a primitive 1579 * type. 1580 * 1581 * @param value the value to be checked 1582 * @return a flag whether the value is a scalar 1583 * @since 1.7 1584 */ 1585 protected boolean isScalarValue(Object value) 1586 { 1587 return ClassUtils.wrapperToPrimitive(value.getClass()) != null; 1588 } 1589 1590 /** 1591 * Copies the content of the specified configuration into this 1592 * configuration. If the specified configuration contains a key that is also 1593 * present in this configuration, the value of this key will be replaced by 1594 * the new value. <em>Note:</em> This method won't work well when copying 1595 * hierarchical configurations because it is not able to copy information 1596 * about the properties' structure (i.e. the parent-child-relationships will 1597 * get lost). So when dealing with hierarchical configuration objects their 1598 * {@link BaseHierarchicalConfiguration#clone() clone()} methods 1599 * should be used. 1600 * 1601 * @param c the configuration to copy (can be <b>null</b>, then this 1602 * operation will have no effect) 1603 * @since 1.5 1604 */ 1605 public void copy(Configuration c) 1606 { 1607 if (c != null) 1608 { 1609 c.lock(LockMode.READ); 1610 try 1611 { 1612 for (Iterator<String> it = c.getKeys(); it.hasNext();) 1613 { 1614 String key = it.next(); 1615 Object value = encodeForCopy(c.getProperty(key)); 1616 setProperty(key, value); 1617 } 1618 } 1619 finally 1620 { 1621 c.unlock(LockMode.READ); 1622 } 1623 } 1624 } 1625 1626 /** 1627 * Appends the content of the specified configuration to this configuration. 1628 * The values of all properties contained in the specified configuration 1629 * will be appended to this configuration. So if a property is already 1630 * present in this configuration, its new value will be a union of the 1631 * values in both configurations. <em>Note:</em> This method won't work 1632 * well when appending hierarchical configurations because it is not able to 1633 * copy information about the properties' structure (i.e. the 1634 * parent-child-relationships will get lost). So when dealing with 1635 * hierarchical configuration objects their 1636 * {@link BaseHierarchicalConfiguration#clone() clone()} methods 1637 * should be used. 1638 * 1639 * @param c the configuration to be appended (can be <b>null</b>, then this 1640 * operation will have no effect) 1641 * @since 1.5 1642 */ 1643 public void append(Configuration c) 1644 { 1645 if (c != null) 1646 { 1647 c.lock(LockMode.READ); 1648 try 1649 { 1650 for (Iterator<String> it = c.getKeys(); it.hasNext();) 1651 { 1652 String key = it.next(); 1653 Object value = encodeForCopy(c.getProperty(key)); 1654 addProperty(key, value); 1655 } 1656 } 1657 finally 1658 { 1659 c.unlock(LockMode.READ); 1660 } 1661 } 1662 } 1663 1664 /** 1665 * Returns a configuration with the same content as this configuration, but 1666 * with all variables replaced by their actual values. This method tries to 1667 * clone the configuration and then perform interpolation on all properties. 1668 * So property values of the form <code>${var}</code> will be resolved as 1669 * far as possible (if a variable cannot be resolved, it remains unchanged). 1670 * This operation is useful if the content of a configuration is to be 1671 * exported or processed by an external component that does not support 1672 * variable interpolation. 1673 * 1674 * @return a configuration with all variables interpolated 1675 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if this 1676 * configuration cannot be cloned 1677 * @since 1.5 1678 */ 1679 public Configuration interpolatedConfiguration() 1680 { 1681 // first clone this configuration 1682 AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils 1683 .cloneConfiguration(this); 1684 1685 // now perform interpolation 1686 c.setListDelimiterHandler(new DisabledListDelimiterHandler()); 1687 for (Iterator<String> it = getKeys(); it.hasNext();) 1688 { 1689 String key = it.next(); 1690 c.setProperty(key, getList(key)); 1691 } 1692 1693 c.setListDelimiterHandler(getListDelimiterHandler()); 1694 return c; 1695 } 1696 1697 /** 1698 * Initializes the logger. Supports <b>null</b> input. This method can be 1699 * called by derived classes in order to enable logging. 1700 * 1701 * @param log the logger 1702 * @since 2.0 1703 */ 1704 protected final void initLogger(ConfigurationLogger log) 1705 { 1706 this.log = (log != null) ? log : ConfigurationLogger.newDummyLogger(); 1707 } 1708 1709 /** 1710 * Encodes a property value so that it can be added to this configuration. 1711 * This method deals with list delimiters. The passed in object has to be 1712 * escaped so that an add operation yields the same result. If it is a list, 1713 * all of its values have to be escaped. 1714 * 1715 * @param value the value to be encoded 1716 * @return the encoded value 1717 */ 1718 private Object encodeForCopy(Object value) 1719 { 1720 if (value instanceof Collection) 1721 { 1722 return encodeListForCopy((Collection<?>) value); 1723 } 1724 return getListDelimiterHandler().escape(value, 1725 ListDelimiterHandler.NOOP_TRANSFORMER); 1726 } 1727 1728 /** 1729 * Encodes a list with property values so that it can be added to this 1730 * configuration. This method calls {@code encodeForCopy()} for all list 1731 * elements. 1732 * 1733 * @param values the list to be encoded 1734 * @return a list with encoded elements 1735 */ 1736 private Object encodeListForCopy(Collection<?> values) 1737 { 1738 List<Object> result = new ArrayList<>(values.size()); 1739 for (Object value : values) 1740 { 1741 result.add(encodeForCopy(value)); 1742 } 1743 return result; 1744 } 1745 1746 /** 1747 * Obtains the property value for the specified key and converts it to the 1748 * given target class. 1749 * 1750 * @param <T> the target type of the conversion 1751 * @param cls the target class 1752 * @param key the key of the desired property 1753 * @param defaultValue a default value 1754 * @return the converted value of this property 1755 * @throws ConversionException if the conversion cannot be performed 1756 */ 1757 private <T> T getAndConvertProperty(Class<T> cls, String key, T defaultValue) 1758 { 1759 Object value = getProperty(key); 1760 try 1761 { 1762 return ObjectUtils.defaultIfNull( 1763 getConversionHandler().to(value, cls, getInterpolator()), 1764 defaultValue); 1765 } 1766 catch (ConversionException cex) 1767 { 1768 // improve error message 1769 throw new ConversionException( 1770 String.format( 1771 "Key '%s' cannot be converted to class %s. Value is: '%s'.", 1772 key, cls.getName(), String.valueOf(value)), cex.getCause()); 1773 } 1774 } 1775 1776 /** 1777 * Helper method for obtaining a property value with a type conversion. 1778 * 1779 * @param <T> the target type of the conversion 1780 * @param cls the target class 1781 * @param key the key of the desired property 1782 * @param defValue a default value 1783 * @param throwOnMissing a flag whether an exception should be thrown for a 1784 * missing value 1785 * @return the converted value 1786 */ 1787 private <T> T convert(Class<T> cls, String key, T defValue, 1788 boolean throwOnMissing) 1789 { 1790 if (cls.isArray()) 1791 { 1792 return cls.cast(convertToArray(cls.getComponentType(), key, defValue)); 1793 } 1794 1795 T result = getAndConvertProperty(cls, key, defValue); 1796 if (result == null) 1797 { 1798 if (throwOnMissing && isThrowExceptionOnMissing()) 1799 { 1800 throwMissingPropertyException(key); 1801 } 1802 return defValue; 1803 } 1804 1805 return result; 1806 } 1807 1808 /** 1809 * Performs a conversion to an array result class. This implementation 1810 * delegates to the {@link ConversionHandler} to perform the actual type 1811 * conversion. If this results in a <b>null</b> result (because the property 1812 * is undefined), the default value is returned. It is checked whether the 1813 * default value is an array with the correct component type. If not, an 1814 * exception is thrown. 1815 * 1816 * @param cls the component class of the array 1817 * @param key the configuration key 1818 * @param defaultValue an optional default value 1819 * @return the converted array 1820 * @throws IllegalArgumentException if the default value is not a compatible 1821 * array 1822 */ 1823 private Object convertToArray(Class<?> cls, String key, Object defaultValue) 1824 { 1825 checkDefaultValueArray(cls, defaultValue); 1826 return ObjectUtils.defaultIfNull(getConversionHandler().toArray( 1827 getProperty(key), cls, getInterpolator()), defaultValue); 1828 } 1829 1830 /** 1831 * Checks an object provided as default value for the {@code getArray()} 1832 * method. Throws an exception if this is not an array with the correct 1833 * component type. 1834 * 1835 * @param cls the component class for the array 1836 * @param defaultValue the default value object to be checked 1837 * @throws IllegalArgumentException if this is not a valid default object 1838 */ 1839 private static void checkDefaultValueArray(Class<?> cls, Object defaultValue) 1840 { 1841 if (defaultValue != null 1842 && (!defaultValue.getClass().isArray() || !cls 1843 .isAssignableFrom(defaultValue.getClass() 1844 .getComponentType()))) 1845 { 1846 throw new IllegalArgumentException( 1847 "The type of the default value (" + defaultValue.getClass() 1848 + ")" + " is not an array of the specified class (" 1849 + cls + ")"); 1850 } 1851 } 1852 1853 /** 1854 * Handles the default collection for a collection conversion. This method 1855 * fills the target collection with the content of the default collection. 1856 * Both collections may be <b>null</b>. 1857 * 1858 * @param target the target collection 1859 * @param defaultValue the default collection 1860 * @return the initialized target collection 1861 */ 1862 private static <T> Collection<T> handleDefaultCollection(Collection<T> target, 1863 Collection<T> defaultValue) 1864 { 1865 if (defaultValue == null) 1866 { 1867 return null; 1868 } 1869 1870 Collection<T> result; 1871 if (target == null) 1872 { 1873 result = new ArrayList<>(defaultValue); 1874 } 1875 else 1876 { 1877 target.addAll(defaultValue); 1878 result = target; 1879 } 1880 return result; 1881 } 1882 1883 /** 1884 * Checks whether the specified value is <b>null</b> and throws an exception 1885 * in this case. This method is used by conversion methods returning 1886 * primitive Java types. Here values to be returned must not be <b>null</b>. 1887 * 1888 * @param <T> the type of the object to be checked 1889 * @param key the key which caused the problem 1890 * @param value the value to be checked 1891 * @return the passed in value for chaining this method call 1892 * @throws NoSuchElementException if the value is <b>null</b> 1893 */ 1894 private static <T> T checkNonNullValue(String key, T value) 1895 { 1896 if (value == null) 1897 { 1898 throwMissingPropertyException(key); 1899 } 1900 return value; 1901 } 1902 1903 /** 1904 * Helper method for throwing an exception for a key that does not map to an 1905 * existing object. 1906 * 1907 * @param key the key (to be part of the error message) 1908 */ 1909 private static void throwMissingPropertyException(String key) 1910 { 1911 throw new NoSuchElementException(String.format( 1912 "Key '%s' does not map to an existing object!", key)); 1913 } 1914}