Coverage Report - org.apache.commons.configuration.AbstractConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractConfiguration
95%
237/250
97%
65/67
2,701
AbstractConfiguration$1
100%
3/3
100%
1/1
2,701
AbstractConfiguration$2
33%
1/3
N/A
2,701
AbstractConfiguration$3
100%
3/3
100%
1/1
2,701
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.configuration;
 19  
 
 20  
 import java.math.BigDecimal;
 21  
 import java.math.BigInteger;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.NoSuchElementException;
 26  
 import java.util.Properties;
 27  
 
 28  
 import org.apache.commons.collections.Predicate;
 29  
 import org.apache.commons.collections.iterators.FilterIterator;
 30  
 import org.apache.commons.configuration.event.ConfigurationErrorEvent;
 31  
 import org.apache.commons.configuration.event.ConfigurationErrorListener;
 32  
 import org.apache.commons.configuration.event.EventSource;
 33  
 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 34  
 import org.apache.commons.lang.BooleanUtils;
 35  
 import org.apache.commons.lang.text.StrLookup;
 36  
 import org.apache.commons.lang.text.StrSubstitutor;
 37  
 import org.apache.commons.logging.Log;
 38  
 import org.apache.commons.logging.impl.NoOpLog;
 39  
 
 40  
 /**
 41  
  * <p>Abstract configuration class. Provides basic functionality but does not
 42  
  * store any data.</p>
 43  
  * <p>If you want to write your own Configuration class then you should
 44  
  * implement only abstract methods from this class. A lot of functionality
 45  
  * needed by typical implementations of the <code>Configuration</conde>
 46  
  * interface is already provided by this base class. Following is a list of
 47  
  * feauters implemented here:
 48  
  * <ul><li>Data conversion support. The various data types required by the
 49  
  * <code>Configuration</code> interface are already handled by this base class.
 50  
  * A concrete sub class only needs to provide a generic <code>getProperty()</code>
 51  
  * method.</li>
 52  
  * <li>Support for variable interpolation. Property values containing special
 53  
  * variable tokens (like <code>${var}</code>) will be replaced by their
 54  
  * corresponding values.</li>
 55  
  * <li>Support for string lists. The values of properties to be added to this
 56  
  * configuration are checked whether they contain a list delimiter character. If
 57  
  * this is the case and if list splitting is enabled, the string is splitted and
 58  
  * multiple values are added for this property. (With the
 59  
  * <code>setListDelimiter()</code> method the delimiter character can be
 60  
  * specified; per default a comma is used. The
 61  
  * <code>setDelimiterParsingDisabled()</code> method can be used to disable
 62  
  * list splitting completely.)</li>
 63  
  * <li>Allows to specify how missing properties are treated. Per default the
 64  
  * get methods returning an object will return <b>null</b> if the searched
 65  
  * property key is not found (and no default value is provided). With the
 66  
  * <code>setThrowExceptionOnMissing()</code> method this behavior can be
 67  
  * changed to throw an exception when a requested property cannot be found.</li>
 68  
  * <li>Basic event support. Whenever this configuration is modified registered
 69  
  * event listeners are notified. Refer to the various <code>EVENT_XXX</code>
 70  
  * constants to get an impression about which event types are supported.</li>
 71  
  * </ul></p>
 72  
  *
 73  
  * @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov </a>
 74  
  * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>
 75  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen </a>
 76  
  * @version $Id: AbstractConfiguration.java,v 1.29 2004/12/02 22:05:52 ebourg
 77  
  * Exp $
 78  
  */
 79  
 public abstract class AbstractConfiguration extends EventSource implements Configuration
 80  
 {
 81  
     /**
 82  
      * Constant for the add property event type.
 83  
      * @since 1.3
 84  
      */
 85  
     public static final int EVENT_ADD_PROPERTY = 1;
 86  
 
 87  
     /**
 88  
      * Constant for the clear property event type.
 89  
      * @since 1.3
 90  
      */
 91  
     public static final int EVENT_CLEAR_PROPERTY = 2;
 92  
 
 93  
     /**
 94  
      * Constant for the set property event type.
 95  
      * @since 1.3
 96  
      */
 97  
     public static final int EVENT_SET_PROPERTY = 3;
 98  
 
 99  
     /**
 100  
      * Constant for the clear configuration event type.
 101  
      * @since 1.3
 102  
      */
 103  
     public static final int EVENT_CLEAR = 4;
 104  
 
 105  
     /**
 106  
      * Constant for the get property event type. This event type is used for
 107  
      * error events.
 108  
      * @since 1.4
 109  
      */
 110  
     public static final int EVENT_READ_PROPERTY = 5;
 111  
 
 112  
     /** start token */
 113  
     protected static final String START_TOKEN = "${";
 114  
 
 115  
     /** end token */
 116  
     protected static final String END_TOKEN = "}";
 117  
 
 118  
     /** The default value for listDelimiter */
 119  57
     private static char defaultListDelimiter = ',';
 120  
 
 121  
     /** Delimiter used to convert single values to lists */
 122  2594
     private char listDelimiter = defaultListDelimiter;
 123  
 
 124  
     /**
 125  
      * When set to true the given configuration delimiter will not be used
 126  
      * while parsing for this configuration.
 127  
      */
 128  
     private boolean delimiterParsingDisabled;
 129  
 
 130  
     /**
 131  
      * Whether the configuration should throw NoSuchElementExceptions or simply
 132  
      * return null when a property does not exist. Defaults to return null.
 133  
      */
 134  
     private boolean throwExceptionOnMissing;
 135  
 
 136  
     /** Stores a reference to the object that handles variable interpolation.*/
 137  
     private StrSubstitutor substitutor;
 138  
 
 139  
     /** Stores the logger.*/
 140  
     private Log log;
 141  
 
 142  
     /**
 143  
      * Creates a new instance of <code>AbstractConfiguration</code>.
 144  
      */
 145  
     public AbstractConfiguration()
 146  2594
     {
 147  2594
         setLogger(null);
 148  2594
     }
 149  
 
 150  
     /**
 151  
      * For configurations extending AbstractConfiguration, allow them to change
 152  
      * the listDelimiter from the default comma (","). This value will be used
 153  
      * only when creating new configurations. Those already created will not be
 154  
      * affected by this change
 155  
      *
 156  
      * @param delimiter The new listDelimiter
 157  
      */
 158  
     public static void setDefaultListDelimiter(char delimiter)
 159  
     {
 160  2
         AbstractConfiguration.defaultListDelimiter = delimiter;
 161  2
     }
 162  
 
 163  
     /**
 164  
      * Sets the default list delimiter.
 165  
      *
 166  
      * @param delimiter the delimiter character
 167  
      * @deprecated Use AbstractConfiguration.setDefaultListDelimiter(char)
 168  
      * instead
 169  
      */
 170  
     public static void setDelimiter(char delimiter)
 171  
     {
 172  0
         setDefaultListDelimiter(delimiter);
 173  0
     }
 174  
 
 175  
     /**
 176  
      * Retrieve the current delimiter. By default this is a comma (",").
 177  
      *
 178  
      * @return The delimiter in use
 179  
      */
 180  
     public static char getDefaultListDelimiter()
 181  
     {
 182  99
         return AbstractConfiguration.defaultListDelimiter;
 183  
     }
 184  
 
 185  
     /**
 186  
      * Returns the default list delimiter.
 187  
      *
 188  
      * @return the default list delimiter
 189  
      * @deprecated Use AbstractConfiguration.getDefaultListDelimiter() instead
 190  
      */
 191  
     public static char getDelimiter()
 192  
     {
 193  0
         return getDefaultListDelimiter();
 194  
     }
 195  
 
 196  
     /**
 197  
      * Change the list delimiter for this configuration.
 198  
      *
 199  
      * Note: this change will only be effective for new parsings. If you
 200  
      * want it to take effect for all loaded properties use the no arg constructor
 201  
      * and call this method before setting the source.
 202  
      *
 203  
      * @param listDelimiter The new listDelimiter
 204  
      */
 205  
     public void setListDelimiter(char listDelimiter)
 206  
     {
 207  368
         this.listDelimiter = listDelimiter;
 208  368
     }
 209  
 
 210  
     /**
 211  
      * Retrieve the delimiter for this configuration. The default
 212  
      * is the value of defaultListDelimiter.
 213  
      *
 214  
      * @return The listDelimiter in use
 215  
      */
 216  
     public char getListDelimiter()
 217  
     {
 218  51970
         return listDelimiter;
 219  
     }
 220  
 
 221  
     /**
 222  
      * Determine if this configuration is using delimiters when parsing
 223  
      * property values to convert them to lists of values. Defaults to false
 224  
      * @return true if delimiters are not being used
 225  
      */
 226  
     public boolean isDelimiterParsingDisabled()
 227  
     {
 228  50968
         return delimiterParsingDisabled;
 229  
     }
 230  
 
 231  
     /**
 232  
      * Set whether this configuration should use delimiters when parsing
 233  
      * property values to convert them to lists of values. By default delimiter
 234  
      * parsing is enabled
 235  
      *
 236  
      * Note: this change will only be effective for new parsings. If you
 237  
      * want it to take effect for all loaded properties use the no arg constructor
 238  
      * and call this method before setting source.
 239  
      * @param delimiterParsingDisabled a flag whether delimiter parsing should
 240  
      * be disabled
 241  
      */
 242  
     public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
 243  
     {
 244  363
         this.delimiterParsingDisabled = delimiterParsingDisabled;
 245  363
     }
 246  
 
 247  
     /**
 248  
      * Allows to set the <code>throwExceptionOnMissing</code> flag. This
 249  
      * flag controls the behavior of property getter methods that return
 250  
      * objects if the requested property is missing. If the flag is set to
 251  
      * <b>false</b> (which is the default value), these methods will return
 252  
      * <b>null</b>. If set to <b>true</b>, they will throw a
 253  
      * <code>NoSuchElementException</code> exception. Note that getter methods
 254  
      * for primitive data types are not affected by this flag.
 255  
      *
 256  
      * @param throwExceptionOnMissing The new value for the property
 257  
      */
 258  
     public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
 259  
     {
 260  753
         this.throwExceptionOnMissing = throwExceptionOnMissing;
 261  753
     }
 262  
 
 263  
     /**
 264  
      * Returns true if missing values throw Exceptions.
 265  
      *
 266  
      * @return true if missing values throw Exceptions
 267  
      */
 268  
     public boolean isThrowExceptionOnMissing()
 269  
     {
 270  725
         return throwExceptionOnMissing;
 271  
     }
 272  
 
 273  
     /**
 274  
      * Returns the object that is responsible for variable interpolation.
 275  
      *
 276  
      * @return the object responsible for variable interpolation
 277  
      * @since 1.4
 278  
      */
 279  
     public synchronized StrSubstitutor getSubstitutor()
 280  
     {
 281  1771
         if (substitutor == null)
 282  
         {
 283  451
             substitutor = new StrSubstitutor(createInterpolator());
 284  
         }
 285  1771
         return substitutor;
 286  
     }
 287  
 
 288  
     /**
 289  
      * Returns the <code>ConfigurationInterpolator</code> object that manages
 290  
      * the lookup objects for resolving variables. <em>Note:</em> If this
 291  
      * object is manipulated (e.g. new lookup objects added), synchronisation
 292  
      * has to be manually ensured. Because
 293  
      * <code>ConfigurationInterpolator</code> is not thread-safe concurrent
 294  
      * access to properties of this configuration instance (which causes the
 295  
      * interpolator to be invoked) may cause race conditions.
 296  
      *
 297  
      * @return the <code>ConfigurationInterpolator</code> associated with this
 298  
      * configuration
 299  
      * @since 1.4
 300  
      */
 301  
     public ConfigurationInterpolator getInterpolator()
 302  
     {
 303  1
         return (ConfigurationInterpolator) getSubstitutor()
 304  
                 .getVariableResolver();
 305  
     }
 306  
 
 307  
     /**
 308  
      * Creates the interpolator object that is responsible for variable
 309  
      * interpolation. This method is invoked on first access of the
 310  
      * interpolation features. It creates a new instance of
 311  
      * <code>ConfigurationInterpolator</code> and sets the default lookup
 312  
      * object to an implementation that queries this configuration.
 313  
      *
 314  
      * @return the newly created interpolator object
 315  
      * @since 1.4
 316  
      */
 317  
     protected ConfigurationInterpolator createInterpolator()
 318  
     {
 319  451
         ConfigurationInterpolator interpol = new ConfigurationInterpolator();
 320  451
         interpol.setDefaultLookup(new StrLookup()
 321  
         {
 322  451
             public String lookup(String var)
 323  
             {
 324  135
                 Object prop = resolveContainerStore(var);
 325  135
                 return (prop != null) ? prop.toString() : null;
 326  
             }
 327  
         });
 328  451
         return interpol;
 329  
     }
 330  
 
 331  
     /**
 332  
      * Returns the logger used by this configuration object.
 333  
      *
 334  
      * @return the logger
 335  
      * @since 1.4
 336  
      */
 337  
     public Log getLogger()
 338  
     {
 339  420
         return log;
 340  
     }
 341  
 
 342  
     /**
 343  
      * Allows to set the logger to be used by this configuration object. This
 344  
      * method makes it possible for clients to exactly control logging behavior.
 345  
      * Per default a logger is set that will ignore all log messages. Derived
 346  
      * classes that want to enable logging should call this method during their
 347  
      * initialization with the logger to be used.
 348  
      *
 349  
      * @param log the new logger
 350  
      * @since 1.4
 351  
      */
 352  
     public void setLogger(Log log)
 353  
     {
 354  3594
         this.log = (log != null) ? log : new NoOpLog();
 355  3594
     }
 356  
 
 357  
     /**
 358  
      * Adds a special
 359  
      * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>
 360  
      * object to this configuration that will log all internal errors. This
 361  
      * method is intended to be used by certain derived classes, for which it is
 362  
      * known that they can fail on property access (e.g.
 363  
      * <code>DatabaseConfiguration</code>).
 364  
      *
 365  
      * @since 1.4
 366  
      */
 367  
     public void addErrorLogListener()
 368  
     {
 369  994
         addErrorListener(new ConfigurationErrorListener()
 370  
         {
 371  994
             public void configurationError(ConfigurationErrorEvent event)
 372  
             {
 373  0
                 getLogger().warn("Internal error", event.getCause());
 374  0
             }
 375  
         });
 376  994
     }
 377  
 
 378  
     /**
 379  
      * {@inheritDoc}
 380  
      */
 381  
     public void addProperty(String key, Object value)
 382  
     {
 383  39489
         fireEvent(EVENT_ADD_PROPERTY, key, value, true);
 384  
 
 385  39489
         if (!isDelimiterParsingDisabled())
 386  
         {
 387  39429
             Iterator it = PropertyConverter.toIterator(value, getListDelimiter());
 388  122027
             while (it.hasNext())
 389  
             {
 390  43171
                 addPropertyDirect(key, it.next());
 391  
             }
 392  
         }
 393  
         else
 394  
         {
 395  60
             addPropertyDirect(key, value);
 396  
         }
 397  
 
 398  39487
         fireEvent(EVENT_ADD_PROPERTY, key, value, false);
 399  39487
     }
 400  
 
 401  
     /**
 402  
      * Adds a key/value pair to the Configuration. Override this method to
 403  
      * provide write acces to underlying Configuration store.
 404  
      *
 405  
      * @param key key to use for mapping
 406  
      * @param value object to store
 407  
      */
 408  
     protected abstract void addPropertyDirect(String key, Object value);
 409  
 
 410  
     /**
 411  
      * interpolate key names to handle ${key} stuff
 412  
      *
 413  
      * @param base string to interpolate
 414  
      *
 415  
      * @return returns the key name with the ${key} substituted
 416  
      */
 417  
     protected String interpolate(String base)
 418  
     {
 419  791
         Object result = interpolate((Object) base);
 420  789
         return (result == null) ? null : result.toString();
 421  
     }
 422  
 
 423  
     /**
 424  
      * Returns the interpolated value. Non String values are returned without change.
 425  
      *
 426  
      * @param value the value to interpolate
 427  
      *
 428  
      * @return returns the value with variables substituted
 429  
      */
 430  
     protected Object interpolate(Object value)
 431  
     {
 432  2535
         return PropertyConverter.interpolate(value, this);
 433  
     }
 434  
 
 435  
     /**
 436  
      * Recursive handler for multple levels of interpolation.
 437  
      *
 438  
      * When called the first time, priorVariables should be null.
 439  
      *
 440  
      * @param base string with the ${key} variables
 441  
      * @param priorVariables serves two purposes: to allow checking for loops,
 442  
      * and creating a meaningful exception message should a loop occur. It's
 443  
      * 0'th element will be set to the value of base from the first call. All
 444  
      * subsequent interpolated variables are added afterward.
 445  
      *
 446  
      * @return the string with the interpolation taken care of
 447  
      * @deprecated Interpolation is now handled by
 448  
      * <code>{@link PropertyConverter}</code>; this method will no longer be
 449  
      * called
 450  
      */
 451  
     protected String interpolateHelper(String base, List priorVariables)
 452  
     {
 453  0
         return base; // just a dummy implementation
 454  
     }
 455  
 
 456  
     /**
 457  
      * {@inheritDoc}
 458  
      */
 459  
     public Configuration subset(String prefix)
 460  
     {
 461  42
         return new SubsetConfiguration(this, prefix, ".");
 462  
     }
 463  
 
 464  
     /**
 465  
      * {@inheritDoc}
 466  
      */
 467  
     public abstract boolean isEmpty();
 468  
 
 469  
     /**
 470  
      * {@inheritDoc}
 471  
      */
 472  
     public abstract boolean containsKey(String key);
 473  
 
 474  
     /**
 475  
      * {@inheritDoc}
 476  
      */
 477  
     public void setProperty(String key, Object value)
 478  
     {
 479  789
         fireEvent(EVENT_SET_PROPERTY, key, value, true);
 480  789
         setDetailEvents(false);
 481  
         try
 482  
         {
 483  789
             clearProperty(key);
 484  789
             addProperty(key, value);
 485  789
         }
 486  
         finally
 487  
         {
 488  0
             setDetailEvents(true);
 489  
         }
 490  789
         fireEvent(EVENT_SET_PROPERTY, key, value, false);
 491  789
     }
 492  
 
 493  
     /**
 494  
      * Removes the specified property from this configuration. This
 495  
      * implementation performs some preparations and then delegates to
 496  
      * <code>clearPropertyDirect()</code>, which will do the real work.
 497  
      *
 498  
      * @param key the key to be removed
 499  
      */
 500  
     public void clearProperty(String key)
 501  
     {
 502  842
         fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
 503  842
         clearPropertyDirect(key);
 504  842
         fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
 505  842
     }
 506  
 
 507  
     /**
 508  
      * Removes the specified property from this configuration. This method is
 509  
      * called by <code>clearProperty()</code> after it has done some
 510  
      * preparations. It should be overriden in sub classes. This base
 511  
      * implementation is just left empty.
 512  
      *
 513  
      * @param key the key to be removed
 514  
      */
 515  
     protected void clearPropertyDirect(String key)
 516  
     {
 517  
         // override in sub classes
 518  0
     }
 519  
 
 520  
     /**
 521  
      * {@inheritDoc}
 522  
      */
 523  
     public void clear()
 524  
     {
 525  29
         fireEvent(EVENT_CLEAR, null, null, true);
 526  29
         setDetailEvents(false);
 527  
         try
 528  
         {
 529  29
             Iterator it = getKeys();
 530  390
             while (it.hasNext())
 531  
             {
 532  332
                 String key = (String) it.next();
 533  332
                 it.remove();
 534  
 
 535  332
                 if (containsKey(key))
 536  
                 {
 537  
                     // workaround for Iterators that do not remove the property on calling remove()
 538  329
                     clearProperty(key);
 539  
                 }
 540  
             }
 541  29
         }
 542  
         finally
 543  
         {
 544  0
             setDetailEvents(true);
 545  
         }
 546  29
         fireEvent(EVENT_CLEAR, null, null, false);
 547  29
     }
 548  
 
 549  
     /**
 550  
      * {@inheritDoc}
 551  
      */
 552  
     public abstract Iterator getKeys();
 553  
 
 554  
     /**
 555  
      * {@inheritDoc}
 556  
      */
 557  
     public Iterator getKeys(final String prefix)
 558  
     {
 559  40
         return new FilterIterator(getKeys(), new Predicate()
 560  
         {
 561  40
             public boolean evaluate(Object obj)
 562  
             {
 563  443
                 String key = (String) obj;
 564  443
                 return key.startsWith(prefix + ".") || key.equals(prefix);
 565  
             }
 566  
         });
 567  
     }
 568  
 
 569  
     /**
 570  
      * {@inheritDoc}
 571  
      */
 572  
     public Properties getProperties(String key)
 573  
     {
 574  4
         return getProperties(key, null);
 575  
     }
 576  
 
 577  
     /**
 578  
      * Get a list of properties associated with the given configuration key.
 579  
      *
 580  
      * @param key The configuration key.
 581  
      * @param defaults Any default values for the returned
 582  
      * <code>Properties</code> object. Ignored if <code>null</code>.
 583  
      *
 584  
      * @return The associated properties if key is found.
 585  
      *
 586  
      * @throws ConversionException is thrown if the key maps to an object that
 587  
      * is not a String/List of Strings.
 588  
      *
 589  
      * @throws IllegalArgumentException if one of the tokens is malformed (does
 590  
      * not contain an equals sign).
 591  
      */
 592  
     public Properties getProperties(String key, Properties defaults)
 593  
     {
 594  
         /*
 595  
          * Grab an array of the tokens for this key.
 596  
          */
 597  4
         String[] tokens = getStringArray(key);
 598  
 
 599  
         /*
 600  
          * Each token is of the form 'key=value'.
 601  
          */
 602  4
         Properties props = defaults == null ? new Properties() : new Properties(defaults);
 603  10
         for (int i = 0; i < tokens.length; i++)
 604  
         {
 605  8
             String token = tokens[i];
 606  8
             int equalSign = token.indexOf('=');
 607  8
             if (equalSign > 0)
 608  
             {
 609  6
                 String pkey = token.substring(0, equalSign).trim();
 610  6
                 String pvalue = token.substring(equalSign + 1).trim();
 611  6
                 props.put(pkey, pvalue);
 612  
             }
 613  2
             else if (tokens.length == 1 && "".equals(token))
 614  
             {
 615  
                 // Semantically equivalent to an empty Properties
 616  
                 // object.
 617  2
                 break;
 618  
             }
 619  
             else
 620  
             {
 621  0
                 throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign");
 622  
             }
 623  
         }
 624  4
         return props;
 625  
     }
 626  
 
 627  
     /**
 628  
      * {@inheritDoc}
 629  
      * @see PropertyConverter#toBoolean(Object)
 630  
      */
 631  
     public boolean getBoolean(String key)
 632  
     {
 633  62
         Boolean b = getBoolean(key, null);
 634  60
         if (b != null)
 635  
         {
 636  58
             return b.booleanValue();
 637  
         }
 638  
         else
 639  
         {
 640  2
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 641  
         }
 642  
     }
 643  
 
 644  
     /**
 645  
      * {@inheritDoc}
 646  
      * @see PropertyConverter#toBoolean(Object)
 647  
      */
 648  
     public boolean getBoolean(String key, boolean defaultValue)
 649  
     {
 650  22
         return getBoolean(key, BooleanUtils.toBooleanObject(defaultValue)).booleanValue();
 651  
     }
 652  
 
 653  
     /**
 654  
      * Obtains the value of the specified key and tries to convert it into a
 655  
      * <code>Boolean</code> object. If the property has no value, the passed
 656  
      * in default value will be used.
 657  
      *
 658  
      * @param key the key of the property
 659  
      * @param defaultValue the default value
 660  
      * @return the value of this key converted to a <code>Boolean</code>
 661  
      * @throws ConversionException if the value cannot be converted to a
 662  
      * <code>Boolean</code>
 663  
      * @see PropertyConverter#toBoolean(Object)
 664  
      */
 665  
     public Boolean getBoolean(String key, Boolean defaultValue)
 666  
     {
 667  118
         Object value = resolveContainerStore(key);
 668  
 
 669  118
         if (value == null)
 670  
         {
 671  35
             return defaultValue;
 672  
         }
 673  
         else
 674  
         {
 675  
             try
 676  
             {
 677  83
                 return PropertyConverter.toBoolean(interpolate(value));
 678  
             }
 679  
             catch (ConversionException e)
 680  
             {
 681  3
                 throw new ConversionException('\'' + key + "' doesn't map to a Boolean object", e);
 682  
             }
 683  
         }
 684  
     }
 685  
 
 686  
     /**
 687  
      * {@inheritDoc}
 688  
      */
 689  
     public byte getByte(String key)
 690  
     {
 691  15
         Byte b = getByte(key, null);
 692  13
         if (b != null)
 693  
         {
 694  11
             return b.byteValue();
 695  
         }
 696  
         else
 697  
         {
 698  2
             throw new NoSuchElementException('\'' + key + " doesn't map to an existing object");
 699  
         }
 700  
     }
 701  
 
 702  
     /**
 703  
      * {@inheritDoc}
 704  
      */
 705  
     public byte getByte(String key, byte defaultValue)
 706  
     {
 707  4
         return getByte(key, new Byte(defaultValue)).byteValue();
 708  
     }
 709  
 
 710  
     /**
 711  
      * {@inheritDoc}
 712  
      */
 713  
     public Byte getByte(String key, Byte defaultValue)
 714  
     {
 715  22
         Object value = resolveContainerStore(key);
 716  
 
 717  22
         if (value == null)
 718  
         {
 719  4
             return defaultValue;
 720  
         }
 721  
         else
 722  
         {
 723  
             try
 724  
             {
 725  18
                 return PropertyConverter.toByte(interpolate(value));
 726  
             }
 727  
             catch (ConversionException e)
 728  
             {
 729  2
                 throw new ConversionException('\'' + key + "' doesn't map to a Byte object", e);
 730  
             }
 731  
         }
 732  
     }
 733  
 
 734  
     /**
 735  
      * {@inheritDoc}
 736  
      */
 737  
     public double getDouble(String key)
 738  
     {
 739  17
         Double d = getDouble(key, null);
 740  15
         if (d != null)
 741  
         {
 742  13
             return d.doubleValue();
 743  
         }
 744  
         else
 745  
         {
 746  2
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 747  
         }
 748  
     }
 749  
 
 750  
     /**
 751  
      * {@inheritDoc}
 752  
      */
 753  
     public double getDouble(String key, double defaultValue)
 754  
     {
 755  11
         return getDouble(key, new Double(defaultValue)).doubleValue();
 756  
     }
 757  
 
 758  
     /**
 759  
      * {@inheritDoc}
 760  
      */
 761  
     public Double getDouble(String key, Double defaultValue)
 762  
     {
 763  31
         Object value = resolveContainerStore(key);
 764  
 
 765  31
         if (value == null)
 766  
         {
 767  11
             return defaultValue;
 768  
         }
 769  
         else
 770  
         {
 771  
             try
 772  
             {
 773  20
                 return PropertyConverter.toDouble(interpolate(value));
 774  
             }
 775  
             catch (ConversionException e)
 776  
             {
 777  2
                 throw new ConversionException('\'' + key + "' doesn't map to a Double object", e);
 778  
             }
 779  
         }
 780  
     }
 781  
 
 782  
     /**
 783  
      * {@inheritDoc}
 784  
      */
 785  
     public float getFloat(String key)
 786  
     {
 787  14
         Float f = getFloat(key, null);
 788  12
         if (f != null)
 789  
         {
 790  10
             return f.floatValue();
 791  
         }
 792  
         else
 793  
         {
 794  2
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 795  
         }
 796  
     }
 797  
 
 798  
     /**
 799  
      * {@inheritDoc}
 800  
      */
 801  
     public float getFloat(String key, float defaultValue)
 802  
     {
 803  7
         return getFloat(key, new Float(defaultValue)).floatValue();
 804  
     }
 805  
 
 806  
     /**
 807  
      * {@inheritDoc}
 808  
      */
 809  
     public Float getFloat(String key, Float defaultValue)
 810  
     {
 811  24
         Object value = resolveContainerStore(key);
 812  
 
 813  24
         if (value == null)
 814  
         {
 815  7
             return defaultValue;
 816  
         }
 817  
         else
 818  
         {
 819  
             try
 820  
             {
 821  17
                 return PropertyConverter.toFloat(interpolate(value));
 822  
             }
 823  
             catch (ConversionException e)
 824  
             {
 825  2
                 throw new ConversionException('\'' + key + "' doesn't map to a Float object", e);
 826  
             }
 827  
         }
 828  
     }
 829  
 
 830  
     /**
 831  
      * {@inheritDoc}
 832  
      */
 833  
     public int getInt(String key)
 834  
     {
 835  48
         Integer i = getInteger(key, null);
 836  48
         if (i != null)
 837  
         {
 838  48
             return i.intValue();
 839  
         }
 840  
         else
 841  
         {
 842  0
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 843  
         }
 844  
     }
 845  
 
 846  
     /**
 847  
      * {@inheritDoc}
 848  
      */
 849  
     public int getInt(String key, int defaultValue)
 850  
     {
 851  3
         Integer i = getInteger(key, null);
 852  
 
 853  3
         if (i == null)
 854  
         {
 855  3
             return defaultValue;
 856  
         }
 857  
 
 858  0
         return i.intValue();
 859  
     }
 860  
 
 861  
     /**
 862  
      * {@inheritDoc}
 863  
      */
 864  
     public Integer getInteger(String key, Integer defaultValue)
 865  
     {
 866  52
         Object value = resolveContainerStore(key);
 867  
 
 868  52
         if (value == null)
 869  
         {
 870  3
             return defaultValue;
 871  
         }
 872  
         else
 873  
         {
 874  
             try
 875  
             {
 876  49
                 return PropertyConverter.toInteger(interpolate(value));
 877  
             }
 878  
             catch (ConversionException e)
 879  
             {
 880  0
                 throw new ConversionException('\'' + key + "' doesn't map to an Integer object", e);
 881  
             }
 882  
         }
 883  
     }
 884  
 
 885  
     /**
 886  
      * {@inheritDoc}
 887  
      */
 888  
     public long getLong(String key)
 889  
     {
 890  15
         Long l = getLong(key, null);
 891  13
         if (l != null)
 892  
         {
 893  11
             return l.longValue();
 894  
         }
 895  
         else
 896  
         {
 897  2
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 898  
         }
 899  
     }
 900  
 
 901  
     /**
 902  
      * {@inheritDoc}
 903  
      */
 904  
     public long getLong(String key, long defaultValue)
 905  
     {
 906  7
         return getLong(key, new Long(defaultValue)).longValue();
 907  
     }
 908  
 
 909  
     /**
 910  
      * {@inheritDoc}
 911  
      */
 912  
     public Long getLong(String key, Long defaultValue)
 913  
     {
 914  26
         Object value = resolveContainerStore(key);
 915  
 
 916  26
         if (value == null)
 917  
         {
 918  7
             return defaultValue;
 919  
         }
 920  
         else
 921  
         {
 922  
             try
 923  
             {
 924  19
                 return PropertyConverter.toLong(interpolate(value));
 925  
             }
 926  
             catch (ConversionException e)
 927  
             {
 928  2
                 throw new ConversionException('\'' + key + "' doesn't map to a Long object", e);
 929  
             }
 930  
         }
 931  
     }
 932  
 
 933  
     /**
 934  
      * {@inheritDoc}
 935  
      */
 936  
     public short getShort(String key)
 937  
     {
 938  17
         Short s = getShort(key, null);
 939  15
         if (s != null)
 940  
         {
 941  13
             return s.shortValue();
 942  
         }
 943  
         else
 944  
         {
 945  2
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 946  
         }
 947  
     }
 948  
 
 949  
     /**
 950  
      * {@inheritDoc}
 951  
      */
 952  
     public short getShort(String key, short defaultValue)
 953  
     {
 954  7
         return getShort(key, new Short(defaultValue)).shortValue();
 955  
     }
 956  
 
 957  
     /**
 958  
      * {@inheritDoc}
 959  
      */
 960  
     public Short getShort(String key, Short defaultValue)
 961  
     {
 962  31
         Object value = resolveContainerStore(key);
 963  
 
 964  31
         if (value == null)
 965  
         {
 966  9
             return defaultValue;
 967  
         }
 968  
         else
 969  
         {
 970  
             try
 971  
             {
 972  22
                 return PropertyConverter.toShort(interpolate(value));
 973  
             }
 974  
             catch (ConversionException e)
 975  
             {
 976  2
                 throw new ConversionException('\'' + key + "' doesn't map to a Short object", e);
 977  
             }
 978  
         }
 979  
     }
 980  
 
 981  
     /**
 982  
      * {@inheritDoc}
 983  
      * @see #setThrowExceptionOnMissing(boolean)
 984  
      */
 985  
     public BigDecimal getBigDecimal(String key)
 986  
     {
 987  7
         BigDecimal number = getBigDecimal(key, null);
 988  5
         if (number != null)
 989  
         {
 990  3
             return number;
 991  
         }
 992  2
         else if (isThrowExceptionOnMissing())
 993  
         {
 994  1
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 995  
         }
 996  
         else
 997  
         {
 998  1
             return null;
 999  
         }
 1000  
     }
 1001  
 
 1002  
     /**
 1003  
      * {@inheritDoc}
 1004  
      */
 1005  
     public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
 1006  
     {
 1007  12
         Object value = resolveContainerStore(key);
 1008  
 
 1009  12
         if (value == null)
 1010  
         {
 1011  4
             return defaultValue;
 1012  
         }
 1013  
         else
 1014  
         {
 1015  
             try
 1016  
             {
 1017  8
                 return PropertyConverter.toBigDecimal(interpolate(value));
 1018  
             }
 1019  
             catch (ConversionException e)
 1020  
             {
 1021  2
                 throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
 1022  
             }
 1023  
         }
 1024  
     }
 1025  
 
 1026  
     /**
 1027  
      * {@inheritDoc}
 1028  
      * @see #setThrowExceptionOnMissing(boolean)
 1029  
      */
 1030  
     public BigInteger getBigInteger(String key)
 1031  
     {
 1032  8
         BigInteger number = getBigInteger(key, null);
 1033  6
         if (number != null)
 1034  
         {
 1035  4
             return number;
 1036  
         }
 1037  2
         else if (isThrowExceptionOnMissing())
 1038  
         {
 1039  1
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 1040  
         }
 1041  
         else
 1042  
         {
 1043  1
             return null;
 1044  
         }
 1045  
     }
 1046  
 
 1047  
     /**
 1048  
      * {@inheritDoc}
 1049  
      */
 1050  
     public BigInteger getBigInteger(String key, BigInteger defaultValue)
 1051  
     {
 1052  13
         Object value = resolveContainerStore(key);
 1053  
 
 1054  13
         if (value == null)
 1055  
         {
 1056  4
             return defaultValue;
 1057  
         }
 1058  
         else
 1059  
         {
 1060  
             try
 1061  
             {
 1062  9
                 return PropertyConverter.toBigInteger(interpolate(value));
 1063  
             }
 1064  
             catch (ConversionException e)
 1065  
             {
 1066  2
                 throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
 1067  
             }
 1068  
         }
 1069  
     }
 1070  
 
 1071  
     /**
 1072  
      * {@inheritDoc}
 1073  
      * @see #setThrowExceptionOnMissing(boolean)
 1074  
      */
 1075  
     public String getString(String key)
 1076  
     {
 1077  613
         String s = getString(key, null);
 1078  611
         if (s != null)
 1079  
         {
 1080  396
             return s;
 1081  
         }
 1082  215
         else if (isThrowExceptionOnMissing())
 1083  
         {
 1084  7
             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
 1085  
         }
 1086  
         else
 1087  
         {
 1088  208
             return null;
 1089  
         }
 1090  
     }
 1091  
 
 1092  
     /**
 1093  
      * {@inheritDoc}
 1094  
      */
 1095  
     public String getString(String key, String defaultValue)
 1096  
     {
 1097  689
         Object value = resolveContainerStore(key);
 1098  
 
 1099  689
         if (value instanceof String)
 1100  
         {
 1101  466
             return interpolate((String) value);
 1102  
         }
 1103  223
         else if (value == null)
 1104  
         {
 1105  223
             return interpolate(defaultValue);
 1106  
         }
 1107  
         else
 1108  
         {
 1109  0
             throw new ConversionException('\'' + key + "' doesn't map to a String object");
 1110  
         }
 1111  
     }
 1112  
 
 1113  
     /**
 1114  
      * Get an array of strings associated with the given configuration key.
 1115  
      * If the key doesn't map to an existing object, an empty array is returned.
 1116  
      * If a property is added to a configuration, it is checked whether it
 1117  
      * contains multiple values. This is obvious if the added object is a list
 1118  
      * or an array. For strings it is checked whether the string contains the
 1119  
      * list delimiter character that can be specified using the
 1120  
      * <code>setListDelimiter()</code> method. If this is the case, the string
 1121  
      * is splitted at these positions resulting in a property with multiple
 1122  
      * values.
 1123  
      *
 1124  
      * @param key The configuration key.
 1125  
      * @return The associated string array if key is found.
 1126  
      *
 1127  
      * @throws ConversionException is thrown if the key maps to an
 1128  
      *         object that is not a String/List of Strings.
 1129  
      * @see #setListDelimiter(char)
 1130  
      * @see #setDelimiterParsingDisabled(boolean)
 1131  
      */
 1132  
     public String[] getStringArray(String key)
 1133  
     {
 1134  21
         Object value = getProperty(key);
 1135  
 
 1136  
         String[] array;
 1137  
 
 1138  21
         if (value instanceof String)
 1139  
         {
 1140  11
             array = new String[1];
 1141  
 
 1142  11
             array[0] = interpolate((String) value);
 1143  
         }
 1144  10
         else if (value instanceof List)
 1145  
         {
 1146  8
             List list = (List) value;
 1147  8
             array = new String[list.size()];
 1148  
 
 1149  30
             for (int i = 0; i < array.length; i++)
 1150  
             {
 1151  22
                 array[i] = interpolate((String) list.get(i));
 1152  
             }
 1153  
         }
 1154  2
         else if (value == null)
 1155  
         {
 1156  2
             array = new String[0];
 1157  
         }
 1158  
         else
 1159  
         {
 1160  0
             throw new ConversionException('\'' + key + "' doesn't map to a String/List object");
 1161  
         }
 1162  21
         return array;
 1163  
     }
 1164  
 
 1165  
     /**
 1166  
      * {@inheritDoc}
 1167  
      * @see #getStringArray(String)
 1168  
      */
 1169  
     public List getList(String key)
 1170  
     {
 1171  296
         return getList(key, new ArrayList());
 1172  
     }
 1173  
 
 1174  
     /**
 1175  
      * {@inheritDoc}
 1176  
      */
 1177  
     public List getList(String key, List defaultValue)
 1178  
     {
 1179  253
         Object value = getProperty(key);
 1180  
         List list;
 1181  
 
 1182  253
         if (value instanceof String)
 1183  
         {
 1184  49
             list = new ArrayList(1);
 1185  49
             list.add(interpolate((String) value));
 1186  
         }
 1187  204
         else if (value instanceof List)
 1188  
         {
 1189  147
             list = new ArrayList();
 1190  147
             List l = (List) value;
 1191  
 
 1192  
             // add the interpolated elements in the new list
 1193  147
             Iterator it = l.iterator();
 1194  876
             while (it.hasNext())
 1195  
             {
 1196  582
                 list.add(interpolate(it.next()));
 1197  
             }
 1198  
 
 1199  
         }
 1200  57
         else if (value == null)
 1201  
         {
 1202  55
             list = defaultValue;
 1203  
         }
 1204  
         else
 1205  
         {
 1206  2
             throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a "
 1207  
                     + value.getClass().getName());
 1208  
         }
 1209  251
         return list;
 1210  
     }
 1211  
 
 1212  
     /**
 1213  
      * Returns an object from the store described by the key. If the value is a
 1214  
      * List object, replace it with the first object in the list.
 1215  
      *
 1216  
      * @param key The property key.
 1217  
      *
 1218  
      * @return value Value, transparently resolving a possible List dependency.
 1219  
      */
 1220  
     protected Object resolveContainerStore(String key)
 1221  
     {
 1222  1182
         Object value = getProperty(key);
 1223  1182
         if (value != null)
 1224  
         {
 1225  860
             if (value instanceof List)
 1226  
             {
 1227  11
                 List list = (List) value;
 1228  11
                 value = list.isEmpty() ? null : list.get(0);
 1229  
             }
 1230  849
             else if (value instanceof Object[])
 1231  
             {
 1232  1
                 Object[] array = (Object[]) value;
 1233  1
                 value = array.length == 0 ? null : array[0];
 1234  
             }
 1235  848
             else if (value instanceof boolean[])
 1236  
             {
 1237  1
                 boolean[] array = (boolean[]) value;
 1238  1
                 value = array.length == 0 ? null : array[0] ? Boolean.TRUE : Boolean.FALSE;
 1239  
             }
 1240  847
             else if (value instanceof byte[])
 1241  
             {
 1242  1
                 byte[] array = (byte[]) value;
 1243  1
                 value = array.length == 0 ? null : new Byte(array[0]);
 1244  
             }
 1245  846
             else if (value instanceof short[])
 1246  
             {
 1247  1
                 short[] array = (short[]) value;
 1248  1
                 value = array.length == 0 ? null : new Short(array[0]);
 1249  
             }
 1250  845
             else if (value instanceof int[])
 1251  
             {
 1252  1
                 int[] array = (int[]) value;
 1253  1
                 value = array.length == 0 ? null : new Integer(array[0]);
 1254  
             }
 1255  844
             else if (value instanceof long[])
 1256  
             {
 1257  1
                 long[] array = (long[]) value;
 1258  1
                 value = array.length == 0 ? null : new Long(array[0]);
 1259  
             }
 1260  843
             else if (value instanceof float[])
 1261  
             {
 1262  1
                 float[] array = (float[]) value;
 1263  1
                 value = array.length == 0 ? null : new Float(array[0]);
 1264  
             }
 1265  842
             else if (value instanceof double[])
 1266  
             {
 1267  1
                 double[] array = (double[]) value;
 1268  1
                 value = array.length == 0 ? null : new Double(array[0]);
 1269  
             }
 1270  
         }
 1271  
 
 1272  1182
         return value;
 1273  
     }
 1274  
 
 1275  
 }