View Javadoc

    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  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      private static char defaultListDelimiter = ',';
    85
    86      /*** Delimiter used to convert single values to lists */
    87      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         AbstractConfiguration.defaultListDelimiter = delimiter;
    112     }
    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         setDefaultListDelimiter(delimiter);
    124     }
    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         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         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         this.listDelimiter = listDelimiter;
    159     }
    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         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         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         this.delimiterParsingDisabled = delimiterParsingDisabled;
    196     }
    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         this.throwExceptionOnMissing = throwExceptionOnMissing;
    212     }
    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         return throwExceptionOnMissing;
    222     }
    223
    224     /***
    225      * {@inheritDoc}
    226      */
    227     public void addProperty(String key, Object value)
    228     {
    229         fireEvent(EVENT_ADD_PROPERTY, key, value, true);
    230
    231         if (!isDelimiterParsingDisabled())
    232         {
    233             Iterator it = PropertyConverter.toIterator(value, getListDelimiter());
    234             while (it.hasNext())
    235             {
    236                 addPropertyDirect(key, it.next());
    237             }
    238         }
    239         else
    240         {
    241             addPropertyDirect(key, value);
    242         }
    243
    244         fireEvent(EVENT_ADD_PROPERTY, key, value, false);
    245     }
    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         Object result = interpolate((Object) base);
    266         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         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         return base; // just a dummy implementation
    300     }
    301
    302     /***
    303      * {@inheritDoc}
    304      */
    305     public Configuration subset(String prefix)
    306     {
    307         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         fireEvent(EVENT_SET_PROPERTY, key, value, true);
    326         setDetailEvents(false);
    327         try
    328         {
    329             clearProperty(key);
    330             addProperty(key, value);
    331         }
    332         finally
    333         {
    334             setDetailEvents(true);
    335         }
    336         fireEvent(EVENT_SET_PROPERTY, key, value, false);
    337     }
    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         fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
    349         clearPropertyDirect(key);
    350         fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
    351     }
    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     }
    365
    366     /***
    367      * {@inheritDoc}
    368      */
    369     public void clear()
    370     {
    371         fireEvent(EVENT_CLEAR, null, null, true);
    372         setDetailEvents(false);
    373         try
    374         {
    375             Iterator it = getKeys();
    376             while (it.hasNext())
    377             {
    378                 String key = (String) it.next();
    379                 it.remove();
    380
    381                 if (containsKey(key))
    382                 {
    383                     // workaround for Iterators that do not remove the property on calling remove()
    384                     clearProperty(key);
    385                 }
    386             }
    387         }
    388         finally
    389         {
    390             setDetailEvents(true);
    391         }
    392         fireEvent(EVENT_CLEAR, null, null, false);
    393     }
    394
    395     /***
    396      * {@inheritDoc}
    397      */
    398     public abstract Iterator getKeys();
    399
    400     /***
    401      * {@inheritDoc}
    402      */
    403     public Iterator getKeys(final String prefix)
    404     {
    405         return new FilterIterator(getKeys(), new Predicate()
    406         {
    407             public boolean evaluate(Object obj)
    408             {
    409                 String key = (String) obj;
    410                 return key.startsWith(prefix + ".") || key.equals(prefix);
    411             }
    412         });
    413     }
    414
    415     /***
    416      * {@inheritDoc}
    417      */
    418     public Properties getProperties(String key)
    419     {
    420         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         String[] tokens = getStringArray(key);
    444
    445         /*
    446          * Each token is of the form 'key=value'.
    447          */
    448         Properties props = defaults == null ? new Properties() : new Properties(defaults);
    449         for (int i = 0; i < tokens.length; i++)
    450         {
    451             String token = tokens[i];
    452             int equalSign = token.indexOf('=');
    453             if (equalSign > 0)
    454             {
    455                 String pkey = token.substring(0, equalSign).trim();
    456                 String pvalue = token.substring(equalSign + 1).trim();
    457                 props.put(pkey, pvalue);
    458             }
    459             else if (tokens.length == 1 && "".equals(token))
    460             {
    461                 // Semantically equivalent to an empty Properties
    462                 // object.
    463                 break;
    464             }
    465             else
    466             {
    467                 throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign");
    468             }
    469         }
    470         return props;
    471     }
    472
    473     /***
    474      * {@inheritDoc}
    475      * @see PropertyConverter#toBoolean(Object)
    476      */
    477     public boolean getBoolean(String key)
    478     {
    479         Boolean b = getBoolean(key, null);
    480         if (b != null)
    481         {
    482             return b.booleanValue();
    483         }
    484         else
    485         {
    486             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         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         Object value = resolveContainerStore(key);
    514
    515         if (value == null)
    516         {
    517             return defaultValue;
    518         }
    519         else
    520         {
    521             try
    522             {
    523                 return PropertyConverter.toBoolean(interpolate(value));
    524             }
    525             catch (ConversionException e)
    526             {
    527                 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         Byte b = getByte(key, null);
    538         if (b != null)
    539         {
    540             return b.byteValue();
    541         }
    542         else
    543         {
    544             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         return getByte(key, new Byte(defaultValue)).byteValue();
    554     }
    555
    556     /***
    557      * {@inheritDoc}
    558      */
    559     public Byte getByte(String key, Byte defaultValue)
    560     {
    561         Object value = resolveContainerStore(key);
    562
    563         if (value == null)
    564         {
    565             return defaultValue;
    566         }
    567         else
    568         {
    569             try
    570             {
    571                 return PropertyConverter.toByte(interpolate(value));
    572             }
    573             catch (ConversionException e)
    574             {
    575                 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         Double d = getDouble(key, null);
    586         if (d != null)
    587         {
    588             return d.doubleValue();
    589         }
    590         else
    591         {
    592             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         return getDouble(key, new Double(defaultValue)).doubleValue();
    602     }
    603
    604     /***
    605      * {@inheritDoc}
    606      */
    607     public Double getDouble(String key, Double defaultValue)
    608     {
    609         Object value = resolveContainerStore(key);
    610
    611         if (value == null)
    612         {
    613             return defaultValue;
    614         }
    615         else
    616         {
    617             try
    618             {
    619                 return PropertyConverter.toDouble(interpolate(value));
    620             }
    621             catch (ConversionException e)
    622             {
    623                 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         Float f = getFloat(key, null);
    634         if (f != null)
    635         {
    636             return f.floatValue();
    637         }
    638         else
    639         {
    640             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         return getFloat(key, new Float(defaultValue)).floatValue();
    650     }
    651
    652     /***
    653      * {@inheritDoc}
    654      */
    655     public Float getFloat(String key, Float defaultValue)
    656     {
    657         Object value = resolveContainerStore(key);
    658
    659         if (value == null)
    660         {
    661             return defaultValue;
    662         }
    663         else
    664         {
    665             try
    666             {
    667                 return PropertyConverter.toFloat(interpolate(value));
    668             }
    669             catch (ConversionException e)
    670             {
    671                 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         Integer i = getInteger(key, null);
    682         if (i != null)
    683         {
    684             return i.intValue();
    685         }
    686         else
    687         {
    688             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         Integer i = getInteger(key, null);
    698
    699         if (i == null)
    700         {
    701             return defaultValue;
    702         }
    703
    704         return i.intValue();
    705     }
    706
    707     /***
    708      * {@inheritDoc}
    709      */
    710     public Integer getInteger(String key, Integer defaultValue)
    711     {
    712         Object value = resolveContainerStore(key);
    713
    714         if (value == null)
    715         {
    716             return defaultValue;
    717         }
    718         else
    719         {
    720             try
    721             {
    722                 return PropertyConverter.toInteger(interpolate(value));
    723             }
    724             catch (ConversionException e)
    725             {
    726                 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         Long l = getLong(key, null);
    737         if (l != null)
    738         {
    739             return l.longValue();
    740         }
    741         else
    742         {
    743             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         return getLong(key, new Long(defaultValue)).longValue();
    753     }
    754
    755     /***
    756      * {@inheritDoc}
    757      */
    758     public Long getLong(String key, Long defaultValue)
    759     {
    760         Object value = resolveContainerStore(key);
    761
    762         if (value == null)
    763         {
    764             return defaultValue;
    765         }
    766         else
    767         {
    768             try
    769             {
    770                 return PropertyConverter.toLong(interpolate(value));
    771             }
    772             catch (ConversionException e)
    773             {
    774                 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         Short s = getShort(key, null);
    785         if (s != null)
    786         {
    787             return s.shortValue();
    788         }
    789         else
    790         {
    791             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         return getShort(key, new Short(defaultValue)).shortValue();
    801     }
    802
    803     /***
    804      * {@inheritDoc}
    805      */
    806     public Short getShort(String key, Short defaultValue)
    807     {
    808         Object value = resolveContainerStore(key);
    809
    810         if (value == null)
    811         {
    812             return defaultValue;
    813         }
    814         else
    815         {
    816             try
    817             {
    818                 return PropertyConverter.toShort(interpolate(value));
    819             }
    820             catch (ConversionException e)
    821             {
    822                 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         BigDecimal number = getBigDecimal(key, null);
    833         if (number != null)
    834         {
    835             return number;
    836         }
    837         else if (isThrowExceptionOnMissing())
    838         {
    839             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    840         }
    841         else
    842         {
    843             return null;
    844         }
    845     }
    846
    847     /***
    848      * {@inheritDoc}
    849      */
    850     public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
    851     {
    852         Object value = resolveContainerStore(key);
    853
    854         if (value == null)
    855         {
    856             return defaultValue;
    857         }
    858         else
    859         {
    860             try
    861             {
    862                 return PropertyConverter.toBigDecimal(interpolate(value));
    863             }
    864             catch (ConversionException e)
    865             {
    866                 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         BigInteger number = getBigInteger(key, null);
    877         if (number != null)
    878         {
    879             return number;
    880         }
    881         else if (isThrowExceptionOnMissing())
    882         {
    883             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    884         }
    885         else
    886         {
    887             return null;
    888         }
    889     }
    890
    891     /***
    892      * {@inheritDoc}
    893      */
    894     public BigInteger getBigInteger(String key, BigInteger defaultValue)
    895     {
    896         Object value = resolveContainerStore(key);
    897
    898         if (value == null)
    899         {
    900             return defaultValue;
    901         }
    902         else
    903         {
    904             try
    905             {
    906                 return PropertyConverter.toBigInteger(interpolate(value));
    907             }
    908             catch (ConversionException e)
    909             {
    910                 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         String s = getString(key, null);
    921         if (s != null)
    922         {
    923             return s;
    924         }
    925         else if (isThrowExceptionOnMissing())
    926         {
    927             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    928         }
    929         else
    930         {
    931             return null;
    932         }
    933     }
    934
    935     /***
    936      * {@inheritDoc}
    937      */
    938     public String getString(String key, String defaultValue)
    939     {
    940         Object value = resolveContainerStore(key);
    941
    942         if (value instanceof String)
    943         {
    944             return interpolate((String) value);
    945         }
    946         else if (value == null)
    947         {
    948             return interpolate(defaultValue);
    949         }
    950         else
    951         {
    952             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         Object value = getProperty(key);
    962
    963         String[] array;
    964
    965         if (value instanceof String)
    966         {
    967             array = new String[1];
    968
    969             array[0] = interpolate((String) value);
    970         }
    971         else if (value instanceof List)
    972         {
    973             List list = (List) value;
    974             array = new String[list.size()];
    975
    976             for (int i = 0; i < array.length; i++)
    977             {
    978                 array[i] = interpolate((String) list.get(i));
    979             }
    980         }
    981         else if (value == null)
    982         {
    983             array = new String[0];
    984         }
    985         else
    986         {
    987             throw new ConversionException('\'' + key + "' doesn't map to a String/List object");
    988         }
    989         return array;
    990     }
    991
    992     /***
    993      * {@inheritDoc}
    994      */
    995     public List getList(String key)
    996     {
    997         return getList(key, new ArrayList());
    998     }
    999
    1000     /***
    1001      * {@inheritDoc}
    1002      */
    1003     public List getList(String key, List defaultValue)
    1004     {
    1005         Object value = getProperty(key);
    1006         List list;
    1007
    1008         if (value instanceof String)
    1009         {
    1010             list = new ArrayList(1);
    1011             list.add(interpolate((String) value));
    1012         }
    1013         else if (value instanceof List)
    1014         {
    1015             list = new ArrayList();
    1016             List l = (List) value;
    1017
    1018             // add the interpolated elements in the new list
    1019             Iterator it = l.iterator();
    1020             while (it.hasNext())
    1021             {
    1022                 list.add(interpolate(it.next()));
    1023             }
    1024
    1025         }
    1026         else if (value == null)
    1027         {
    1028             list = defaultValue;
    1029         }
    1030         else
    1031         {
    1032             throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a "
    1033                     + value.getClass().getName());
    1034         }
    1035         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         Object value = getProperty(key);
    1049         if (value != null)
    1050         {
    1051             if (value instanceof List)
    1052             {
    1053                 List list = (List) value;
    1054                 value = list.isEmpty() ? null : list.get(0);
    1055             }
    1056             else if (value instanceof Object[])
    1057             {
    1058                 Object[] array = (Object[]) value;
    1059                 value = array.length == 0 ? null : array[0];
    1060             }
    1061             else if (value instanceof boolean[])
    1062             {
    1063                 boolean[] array = (boolean[]) value;
    1064                 value = array.length == 0 ? null : array[0] ? Boolean.TRUE : Boolean.FALSE;
    1065             }
    1066             else if (value instanceof byte[])
    1067             {
    1068                 byte[] array = (byte[]) value;
    1069                 value = array.length == 0 ? null : new Byte(array[0]);
    1070             }
    1071             else if (value instanceof short[])
    1072             {
    1073                 short[] array = (short[]) value;
    1074                 value = array.length == 0 ? null : new Short(array[0]);
    1075             }
    1076             else if (value instanceof int[])
    1077             {
    1078                 int[] array = (int[]) value;
    1079                 value = array.length == 0 ? null : new Integer(array[0]);
    1080             }
    1081             else if (value instanceof long[])
    1082             {
    1083                 long[] array = (long[]) value;
    1084                 value = array.length == 0 ? null : new Long(array[0]);
    1085             }
    1086             else if (value instanceof float[])
    1087             {
    1088                 float[] array = (float[]) value;
    1089                 value = array.length == 0 ? null : new Float(array[0]);
    1090             }
    1091             else if (value instanceof double[])
    1092             {
    1093                 double[] array = (double[]) value;
    1094                 value = array.length == 0 ? null : new Double(array[0]);
    1095             }
    1096         }
    1097
    1098         return value;
    1099     }
    1100
    1101 }