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