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