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