    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.configuration;
    019
    020    import java.awt.Color;
    021    import java.io.Serializable;
    022    import java.lang.reflect.Array;
    023    import java.math.BigDecimal;
    024    import java.math.BigInteger;
    025    import java.net.URL;
    026    import java.util.ArrayList;
    027    import java.util.Calendar;
    028    import java.util.Collection;
    029    import java.util.Date;
    030    import java.util.Iterator;
    031    import java.util.List;
    032    import java.util.Locale;
    033    import java.util.NoSuchElementException;
    034
    035    import org.apache.commons.lang.ClassUtils;
    036    import org.apache.commons.lang.StringUtils;
    037
    038    /**
    039     * Decorator providing additional getters for any Configuration. This extended
    040     * Configuration supports more types:
    041     * <ul>
    042     *   <li>{@link java.net.URL}</li>
    043     *   <li>{@link java.util.Locale}</li>
    044     *   <li>{@link java.util.Date}</li>
    045     *   <li>{@link java.util.Calendar}</li>
    046     *   <li>{@link java.awt.Color}</li>
    047     *   <li>{@link java.net.InetAddress}</li>
    048     *   <li>{@link javax.mail.internet.InternetAddress} (requires Javamail in the classpath)</li>
    049     *   <li>{@link java.lang.Enum} (Java 5 enumeration types)</li>
    050     * </ul>
    051     *
    052     * Lists and arrays are available for all types.
    053     *
    054     * <h4>Example</h4>
    055     *
    056     * Configuration file <tt>config.properties</tt>:
    057     * <pre>
    058     * title.color = #0000FF
    059     * remote.host = 192.168.0.53
    060     * default.locales = fr,en,de
    061     * email.contact = ebourg@apache.org, oheger@apache.org
    062     * </pre>
    063     *
    064     * Usage:
    065     *
    066     * <pre>
    067     * DataConfiguration config = new DataConfiguration(new PropertiesConfiguration("config.properties"));
    068     *
    069     * // retrieve a property using a specialized getter
    070     * Color color = config.getColor("title.color");
    071     *
    072     * // retrieve a property using a generic getter
    073     * InetAddress host = (InetAddress) config.get(InetAddress.class, "remote.host");
    074     * Locale[] locales = (Locale[]) config.getArray(Locale.class, "default.locales");
    075     * List contacts = config.getList(InternetAddress.class, "email.contact");
    076     * </pre>
    077     *
    078     * <h4>Dates</h4>
    079     *
    080     * Date objects are expected to be formatted with the pattern <tt>yyyy-MM-dd HH:mm:ss</tt>.
    081     * This default format can be changed by specifying another format in the
    082     * getters, or by putting a date format in the configuration under the key
    083     * <tt>org.apache.commons.configuration.format.date</tt>.
    084     *
    085     * @author <a href="ebourg@apache.org">Emmanuel Bourg</a>
    086     * @version $Id: DataConfiguration.java 1234985 2012-01-23 21:09:09Z oheger $
    087     * @since 1.1
    088     */
    089    public class DataConfiguration extends AbstractConfiguration implements Serializable
    090    {
    091        /** The key of the property storing the user defined date format. */
    092        public static final String DATE_FORMAT_KEY = "org.apache.commons.configuration.format.date";
    093
    094        /** The default format for dates. */
    095        public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    096
    097        /**
    098         * The serial version UID.
    099         */
    100        private static final long serialVersionUID = -69011336405718640L;
    101
    102        /** Stores the wrapped configuration.*/
    103        protected Configuration configuration;
    104
    105        /**
    106         * Creates a new instance of {@code DataConfiguration} and sets the
    107         * wrapped configuration.
    108         *
    109         * @param configuration the wrapped configuration
    110         */
    111        public DataConfiguration(Configuration configuration)
    112        {
    113            this.configuration = configuration;
    114        }
    115
    116        /**
    117         * Return the configuration decorated by this DataConfiguration.
    118         *
    119         * @return the wrapped configuration
    120         */
    121        public Configuration getConfiguration()
    122        {
    123            return configuration;
    124        }
    125
    126        public Object getProperty(String key)
    127        {
    128            return configuration.getProperty(key);
    129        }
    130
    131        @Override
    132        protected void addPropertyDirect(String key, Object obj)
    133        {
    134            if (configuration instanceof AbstractConfiguration)
    135            {
    136                ((AbstractConfiguration) configuration).addPropertyDirect(key, obj);
    137            }
    138            else
    139            {
    140                configuration.addProperty(key, obj);
    141            }
    142        }
    143
    144        @Override
    145        public void addProperty(String key, Object value)
    146        {
    147            getConfiguration().addProperty(key, value);
    148        }
    149
    150        public boolean isEmpty()
    151        {
    152            return configuration.isEmpty();
    153        }
    154
    155        public boolean containsKey(String key)
    156        {
    157            return configuration.containsKey(key);
    158        }
    159
    160        @Override
    161        public void clearProperty(String key)
    162        {
    163            configuration.clearProperty(key);
    164        }
    165
    166        @Override
    167        public void setProperty(String key, Object value)
    168        {
    169            configuration.setProperty(key, value);
    170        }
    171
    172        public Iterator<String> getKeys()
    173        {
    174            return configuration.getKeys();
    175        }
    176
    177        /**
    178         * Get an object of the specified type associated with the given
    179         * configuration key. If the key doesn't map to an existing object, the
    180         * method returns null unless {@link #isThrowExceptionOnMissing()} is set
    181         * to <tt>true</tt>.
    182         *
    183         * @param <T> the target type of the value
    184         * @param cls the target class of the value
    185         * @param key the key of the value
    186         *
    187         * @return the value of the requested type for the key
    188         *
    189         * @throws NoSuchElementException if the key doesn't map to an existing
    190         *     object and <tt>throwExceptionOnMissing=true</tt>
    191         * @throws ConversionException if the value is not compatible with the requested type
    192         *
    193         * @since 1.5
    194         */
    195        public <T> T get(Class<T> cls, String key)
    196        {
    197            T value = get(cls, key, null);
    198            if (value != null)
    199            {
    200                return value;
    201            }
    202            else if (isThrowExceptionOnMissing())
    203            {
    204                throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    205            }
    206            else
    207            {
    208                return null;
    209            }
    210        }
    211
    212        /**
    213         * Get an object of the specified type associated with the given
    214         * configuration key. If the key doesn't map to an existing object, the
    215         * default value is returned.
    216         *
    217         * @param <T>          the target type of the value
    218         * @param cls          the target class of the value
    219         * @param key          the key of the value
    220         * @param defaultValue the default value
    221         *
    222         * @return the value of the requested type for the key
    223         *
    224         * @throws ConversionException if the value is not compatible with the requested type
    225         *
    226         * @since 1.5
    227         */
    228        public <T> T get(Class<T> cls, String key, T defaultValue)
    229        {
    230            Object value = resolveContainerStore(key);
    231
    232            if (value == null)
    233            {
    234                return defaultValue;
    235            }
    236
    237            if (Date.class.equals(cls) || Calendar.class.equals(cls))
    238            {
    239                return convert(cls, key, interpolate(value), new String[] {getDefaultDateFormat()});
    240            }
    241            else
    242            {
    243                return convert(cls, key, interpolate(value), null);
    244            }
    245        }
    246
    247        /**
    248         * Get a list of typed objects associated with the given configuration key.
    249         * If the key doesn't map to an existing object, an empty list is returned.
    250         *
    251         * @param <T> the type expected for the elements of the list
    252         * @param cls the class expected for the elements of the list
    253         * @param key The configuration key.
    254         * @return The associated list if the key is found.
    255         *
    256         * @throws ConversionException is thrown if the key maps to an object that
    257         *     is not compatible with a list of the specified class.
    258         *
    259         * @since 1.5
    260         */
    261        public <T> List<T> getList(Class<T> cls, String key)
    262        {
    263            return getList(cls, key, new ArrayList<T>());
    264        }
    265
    266        /**
    267         * Get a list of typed objects associated with the given configuration key.
    268         * If the key doesn't map to an existing object, the default value is
    269         * returned.
    270         *
    271         * @param <T>          the type expected for the elements of the list
    272         * @param cls          the class expected for the elements of the list
    273         * @param key          the configuration key.
    274         * @param defaultValue the default value.
    275         * @return The associated List.
    276         *
    277         * @throws ConversionException is thrown if the key maps to an object that
    278         *     is not compatible with a list of the specified class.
    279         *
    280         * @since 1.5
    281         */
    282        public <T> List<T> getList(Class<T> cls, String key, List<T> defaultValue)
    283        {
    284            Object value = getProperty(key);
    285            Class<?> valueClass = value != null ? value.getClass() : null;
    286
    287            List<T> list;
    288
    289            if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
    290            {
    291                // the value is null or is an empty string
    292                list = defaultValue;
    293            }
    294            else
    295            {
    296                list = new ArrayList<T>();
    297
    298                Object[] params = null;
    299                if (cls.equals(Date.class) || cls.equals(Calendar.class))
    300                {
    301                    params = new Object[] {getDefaultDateFormat()};
    302                }
    303
    304                if (valueClass.isArray())
    305                {
    306                    // get the class of the objects contained in the array
    307                    Class<?> arrayType = valueClass.getComponentType();
    308                    int length = Array.getLength(value);
    309
    310                    if (arrayType.equals(cls)
    311                            || (arrayType.isPrimitive() && cls.equals(ClassUtils.primitiveToWrapper(arrayType))))
    312                    {
    313                        // the value is an array of the specified type, or an array
    314                        // of the primitive type derived from the specified type
    315                        for (int i = 0; i < length; i++)
    316                        {
    317                            list.add(cls.cast(Array.get(value, i)));
    318                        }
    319                    }
    320                    else
    321                    {
    322                        // attempt to convert the elements of the array
    323                        for (int i = 0; i < length; i++)
    324                        {
    325                            list.add(convert(cls, key, interpolate(Array.get(value, i)), params));
    326                        }
    327                    }
    328                }
    329                else if (value instanceof Collection)
    330                {
    331                    Collection<?> values = (Collection<?>) value;
    332
    333                    for (Object o : values)
    334                    {
    335                        list.add(convert(cls, key, interpolate(o), params));
    336                    }
    337                }
    338                else
    339                {
    340                    // attempt to convert a single value
    341                    list.add(convert(cls, key, interpolate(value), params));
    342                }
    343            }
    344
    345            return list;
    346        }
    347
    348        /**
    349         * Get an array of typed objects associated with the given configuration key.
    350         * If the key doesn't map to an existing object, an empty list is returned.
    351         *
    352         * @param cls the type expected for the elements of the array
    353         * @param key The configuration key.
    354         * @return The associated array if the key is found, and the value compatible with the type specified.
    355         *
    356         * @throws ConversionException is thrown if the key maps to an object that
    357         *     is not compatible with a list of the specified class.
    358         *
    359         * @since 1.5
    360         */
    361        public Object getArray(Class<?> cls, String key)
    362        {
    363            return getArray(cls, key, Array.newInstance(cls, 0));
    364        }
    365
    366        /**
    367         * Get an array of typed objects associated with the given configuration key.
    368         * If the key doesn't map to an existing object, the default value is returned.
    369         *
    370         * @param cls          the type expected for the elements of the array
    371         * @param key          the configuration key.
    372         * @param defaultValue the default value
    373         * @return The associated array if the key is found, and the value compatible with the type specified.
    374         *
    375         * @throws ConversionException is thrown if the key maps to an object that
    376         *     is not compatible with an array of the specified class.
    377         * @throws IllegalArgumentException if the default value is not an array of the specified type
    378         *
    379         * @since 1.5
    380         */
    381        public Object getArray(Class<?> cls, String key, Object defaultValue)
    382        {
    383            // check the type of the default value
    384            if (defaultValue != null
    385                    && (!defaultValue.getClass().isArray() || !cls
    386                            .isAssignableFrom(defaultValue.getClass()
    387                                    .getComponentType())))
    388            {
    389                throw new IllegalArgumentException(
    390                        "The type of the default value (" + defaultValue.getClass()
    391                                + ")" + " is not an array of the specified class ("
    392                                + cls + ")");
    393            }
    394
    395            if (cls.isPrimitive())
    396            {
    397                return getPrimitiveArray(cls, key, defaultValue);
    398            }
    399
    400            List<?> list = getList(cls, key);
    401            if (list.isEmpty())
    402            {
    403                return defaultValue;
    404            }
    405            else
    406            {
    407                return list.toArray((Object[]) Array.newInstance(cls, list.size()));
    408            }
    409        }
    410
    411        /**
    412         * Get an array of primitive values associated with the given configuration key.
    413         * If the key doesn't map to an existing object, the default value is returned.
    414         *
    415         * @param cls          the primitive type expected for the elements of the array
    416         * @param key          the configuration key.
    417         * @param defaultValue the default value
    418         * @return The associated array if the key is found, and the value compatible with the type specified.
    419         *
    420         * @throws ConversionException is thrown if the key maps to an object that
    421         *     is not compatible with an array of the specified class.
    422         *
    423         * @since 1.5
    424         */
    425        private Object getPrimitiveArray(Class<?> cls, String key, Object defaultValue)
    426        {
    427            Object value = getProperty(key);
    428            Class<?> valueClass = value != null ? value.getClass() : null;
    429
    430            Object array;
    431
    432            if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
    433            {
    434                // the value is null or is an empty string
    435                array = defaultValue;
    436            }
    437            else
    438            {
    439                if (valueClass.isArray())
    440                {
    441                    // get the class of the objects contained in the array
    442                    Class<?> arrayType = valueClass.getComponentType();
    443                    int length = Array.getLength(value);
    444
    445                    if (arrayType.equals(cls))
    446                    {
    447                        // the value is an array of the same primitive type
    448                        array = value;
    449                    }
    450                    else if (arrayType.equals(ClassUtils.primitiveToWrapper(cls)))
    451                    {
    452                        // the value is an array of the wrapper type derived from the specified primitive type
    453                        array = Array.newInstance(cls, length);
    454
    455                        for (int i = 0; i < length; i++)
    456                        {
    457                            Array.set(array, i, Array.get(value, i));
    458                        }
    459                    }
    460                    else
    461                    {
    462                        throw new ConversionException('\'' + key + "' (" + arrayType + ")"
    463                                + " doesn't map to a compatible array of " + cls);
    464                    }
    465                }
    466                else if (value instanceof Collection)
    467                {
    468                    Collection<?> values = (Collection<?>) value;
    469
    470                    array = Array.newInstance(cls, values.size());
    471
    472                    int i = 0;
    473                    for (Object o : values)
    474                    {
    475                        // This is safe because PropertyConverter can handle
    476                        // conversion to wrapper classes correctly.
    477                        @SuppressWarnings("unchecked")
    478                        Object convertedValue = convert(ClassUtils.primitiveToWrapper(cls), key, interpolate(o), null);
    479                        Array.set(array, i++, convertedValue);
    480                    }
    481                }
    482                else
    483                {
    484                    // attempt to convert a single value
    485                    // This is safe because PropertyConverter can handle
    486                    // conversion to wrapper classes correctly.
    487                    @SuppressWarnings("unchecked")
    488                    Object convertedValue = convert(ClassUtils.primitiveToWrapper(cls), key, interpolate(value), null);
    489
    490                    // create an array of one element
    491                    array = Array.newInstance(cls, 1);
    492                    Array.set(array, 0, convertedValue);
    493                }
    494            }
    495
    496            return array;
    497        }
    498
    499        /**
    500         * Get a list of Boolean objects associated with the given
    501         * configuration key. If the key doesn't map to an existing object
    502         * an empty list is returned.
    503         *
    504         * @param key The configuration key.
    505         * @return The associated Boolean list if the key is found.
    506         *
    507         * @throws ConversionException is thrown if the key maps to an
    508         *         object that is not a list of booleans.
    509         */
    510        public List<Boolean> getBooleanList(String key)
    511        {
    512            return getBooleanList(key, new ArrayList<Boolean>());
    513        }
    514
    515        /**
    516         * Get a list of Boolean objects associated with the given
    517         * configuration key. If the key doesn't map to an existing object,
    518         * the default value is returned.
    519         *
    520         * @param key The configuration key.
    521         * @param defaultValue The default value.
    522         * @return The associated List of Booleans.
    523         *
    524         * @throws ConversionException is thrown if the key maps to an
    525         *         object that is not a list of booleans.
    526         */
    527        public List<Boolean> getBooleanList(String key, List<Boolean> defaultValue)
    528        {
    529             return getList(Boolean.class, key, defaultValue);
    530        }
    531
    532        /**
    533         * Get an array of boolean primitives associated with the given
    534         * configuration key. If the key doesn't map to an existing object
    535         * an empty array is returned.
    536         *
    537         * @param key The configuration key.
    538         * @return The associated boolean array if the key is found.
    539         *
    540         * @throws ConversionException is thrown if the key maps to an
    541         *         object that is not a list of booleans.
    542         */
    543        public boolean[] getBooleanArray(String key)
    544        {
    545            return (boolean[]) getArray(Boolean.TYPE, key);
    546        }
    547
    548        /**
    549         * Get an array of boolean primitives associated with the given
    550         * configuration key. If the key doesn't map to an existing object,
    551         * the default value is returned.
    552         *
    553         * @param key          The configuration key.
    554         * @param defaultValue The default value.
    555         * @return The associated boolean array if the key is found.
    556         *
    557         * @throws ConversionException is thrown if the key maps to an
    558         *         object that is not a list of booleans.
    559         */
    560        public boolean[] getBooleanArray(String key, boolean[] defaultValue)
    561        {
    562            return (boolean[]) getArray(Boolean.TYPE, key, defaultValue);
    563        }
    564
    565        /**
    566         * Get a list of Byte objects associated with the given configuration key.
    567         * If the key doesn't map to an existing object an empty list is returned.
    568         *
    569         * @param key The configuration key.
    570         * @return The associated Byte list if the key is found.
    571         *
    572         * @throws ConversionException is thrown if the key maps to an
    573         *         object that is not a list of bytes.
    574         */
    575        public List<Byte> getByteList(String key)
    576        {
    577            return getByteList(key, new ArrayList<Byte>());
    578        }
    579
    580        /**
    581         * Get a list of Byte objects associated with the given configuration key.
    582         * If the key doesn't map to an existing object, the default value is
    583         * returned.
    584         *
    585         * @param key The configuration key.
    586         * @param defaultValue The default value.
    587         * @return The associated List of Bytes.
    588         *
    589         * @throws ConversionException is thrown if the key maps to an
    590         *         object that is not a list of bytes.
    591         */
    592        public List<Byte> getByteList(String key, List<Byte> defaultValue)
    593        {
    594            return getList(Byte.class, key, defaultValue);
    595        }
    596
    597        /**
    598         * Get an array of byte primitives associated with the given
    599         * configuration key. If the key doesn't map to an existing object
    600         * an empty array is returned.
    601         *
    602         * @param key The configuration key.
    603         * @return The associated byte array if the key is found.
    604         *
    605         * @throws ConversionException is thrown if the key maps to an
    606         *         object that is not a list of bytes.
    607         */
    608        public byte[] getByteArray(String key)
    609        {
    610            return getByteArray(key, new byte[0]);
    611        }
    612
    613        /**
    614         * Get an array of byte primitives associated with the given
    615         * configuration key. If the key doesn't map to an existing object
    616         * an empty array is returned.
    617         *
    618         * @param key The configuration key.
    619         * @param defaultValue the default value, which will be returned if the property is not found
    620         * @return The associated byte array if the key is found.
    621         *
    622         * @throws ConversionException is thrown if the key maps to an
    623         *         object that is not a list of bytes.
    624         */
    625        public byte[] getByteArray(String key, byte[] defaultValue)
    626        {
    627            return (byte[]) getArray(Byte.TYPE, key, defaultValue);
    628        }
    629
    630        /**
    631         * Get a list of Short objects associated with the given configuration key.
    632         * If the key doesn't map to an existing object an empty list is returned.
    633         *
    634         * @param key The configuration key.
    635         * @return The associated Short list if the key is found.
    636         *
    637         * @throws ConversionException is thrown if the key maps to an
    638         *         object that is not a list of shorts.
    639         */
    640        public List<Short> getShortList(String key)
    641        {
    642            return getShortList(key, new ArrayList<Short>());
    643        }
    644
    645        /**
    646         * Get a list of Short objects associated with the given configuration key.
    647         * If the key doesn't map to an existing object, the default value is
    648         * returned.
    649         *
    650         * @param key The configuration key.
    651         * @param defaultValue The default value.
    652         * @return The associated List of Shorts.
    653         *
    654         * @throws ConversionException is thrown if the key maps to an
    655         *         object that is not a list of shorts.
    656         */
    657        public List<Short> getShortList(String key, List<Short> defaultValue)
    658        {
    659            return getList(Short.class, key, defaultValue);
    660        }
    661
    662        /**
    663         * Get an array of short primitives associated with the given
    664         * configuration key. If the key doesn't map to an existing object
    665         * an empty array is returned.
    666         *
    667         * @param key The configuration key.
    668         * @return The associated short array if the key is found.
    669         *
    670         * @throws ConversionException is thrown if the key maps to an
    671         *         object that is not a list of shorts.
    672         */
    673        public short[] getShortArray(String key)
    674        {
    675            return getShortArray(key, new short[0]);
    676        }
    677
    678        /**
    679         * Get an array of short primitives associated with the given
    680         * configuration key. If the key doesn't map to an existing object
    681         * an empty array is returned.
    682         *
    683         * @param key The configuration key.
    684         * @param defaultValue the default value, which will be returned if the property is not found
    685         * @return The associated short array if the key is found.
    686         *
    687         * @throws ConversionException is thrown if the key maps to an
    688         *         object that is not a list of shorts.
    689         */
    690        public short[] getShortArray(String key, short[] defaultValue)
    691        {
    692            return (short[]) getArray(Short.TYPE, key, defaultValue);
    693        }
    694
    695        /**
    696         * Get a list of Integer objects associated with the given
    697         * configuration key. If the key doesn't map to an existing object
    698         * an empty list is returned.
    699         *
    700         * @param key The configuration key.
    701         * @return The associated Integer list if the key is found.
    702         *
    703         * @throws ConversionException is thrown if the key maps to an
    704         *         object that is not a list of integers.
    705         */
    706        public List<Integer> getIntegerList(String key)
    707        {
    708            return getIntegerList(key, new ArrayList<Integer>());
    709        }
    710
    711        /**
    712         * Get a list of Integer objects associated with the given
    713         * configuration key. If the key doesn't map to an existing object,
    714         * the default value is returned.
    715         *
    716         * @param key The configuration key.
    717         * @param defaultValue The default value.
    718         * @return The associated List of Integers.
    719         *
    720         * @throws ConversionException is thrown if the key maps to an
    721         *         object that is not a list of integers.
    722         */
    723        public List<Integer> getIntegerList(String key, List<Integer> defaultValue)
    724        {
    725            return getList(Integer.class, key, defaultValue);
    726        }
    727
    728        /**
    729         * Get an array of int primitives associated with the given
    730         * configuration key. If the key doesn't map to an existing object
    731         * an empty array is returned.
    732         *
    733         * @param key The configuration key.
    734         * @return The associated int array if the key is found.
    735         *
    736         * @throws ConversionException is thrown if the key maps to an
    737         *         object that is not a list of integers.
    738         */
    739        public int[] getIntArray(String key)
    740        {
    741            return getIntArray(key, new int[0]);
    742        }
    743
    744        /**
    745         * Get an array of int primitives associated with the given
    746         * configuration key. If the key doesn't map to an existing object
    747         * an empty array is returned.
    748         *
    749         * @param key The configuration key.
    750         * @param defaultValue the default value, which will be returned if the property is not found
    751         * @return The associated int array if the key is found.
    752         *
    753         * @throws ConversionException is thrown if the key maps to an
    754         *         object that is not a list of integers.
    755         */
    756        public int[] getIntArray(String key, int[] defaultValue)
    757        {
    758            return (int[]) getArray(Integer.TYPE, key, defaultValue);
    759        }
    760
    761        /**
    762         * Get a list of Long objects associated with the given configuration key.
    763         * If the key doesn't map to an existing object an empty list is returned.
    764         *
    765         * @param key The configuration key.
    766         * @return The associated Long list if the key is found.
    767         *
    768         * @throws ConversionException is thrown if the key maps to an
    769         *         object that is not a list of longs.
    770         */
    771        public List<Long> getLongList(String key)
    772        {
    773            return getLongList(key, new ArrayList<Long>());
    774        }
    775
    776        /**
    777         * Get a list of Long objects associated with the given configuration key.
    778         * If the key doesn't map to an existing object, the default value is
    779         * returned.
    780         *
    781         * @param key The configuration key.
    782         * @param defaultValue The default value.
    783         * @return The associated List of Longs.
    784         *
    785         * @throws ConversionException is thrown if the key maps to an
    786         *         object that is not a list of longs.
    787         */
    788        public List<Long> getLongList(String key, List<Long> defaultValue)
    789        {
    790            return getList(Long.class, key, defaultValue);
    791        }
    792
    793        /**
    794         * Get an array of long primitives associated with the given
    795         * configuration key. If the key doesn't map to an existing object
    796         * an empty array is returned.
    797         *
    798         * @param key The configuration key.
    799         * @return The associated long array if the key is found.
    800         *
    801         * @throws ConversionException is thrown if the key maps to an
    802         *         object that is not a list of longs.
    803         */
    804        public long[] getLongArray(String key)
    805        {
    806            return getLongArray(key, new long[0]);
    807        }
    808
    809        /**
    810         * Get an array of long primitives associated with the given
    811         * configuration key. If the key doesn't map to an existing object
    812         * an empty array is returned.
    813         *
    814         * @param key The configuration key.
    815         * @param defaultValue the default value, which will be returned if the property is not found
    816         * @return The associated long array if the key is found.
    817         *
    818         * @throws ConversionException is thrown if the key maps to an
    819         *         object that is not a list of longs.
    820         */
    821        public long[] getLongArray(String key, long[] defaultValue)
    822        {
    823            return (long[]) getArray(Long.TYPE, key, defaultValue);
    824        }
    825
    826        /**
    827         * Get a list of Float objects associated with the given configuration key.
    828         * If the key doesn't map to an existing object an empty list is returned.
    829         *
    830         * @param key The configuration key.
    831         * @return The associated Float list if the key is found.
    832         *
    833         * @throws ConversionException is thrown if the key maps to an
    834         *         object that is not a list of floats.
    835         */
    836        public List<Float> getFloatList(String key)
    837        {
    838            return getFloatList(key, new ArrayList<Float>());
    839        }
    840
    841        /**
    842         * Get a list of Float objects associated with the given
    843         * configuration key. If the key doesn't map to an existing object,
    844         * the default value is returned.
    845         *
    846         * @param key The configuration key.
    847         * @param defaultValue The default value.
    848         * @return The associated List of Floats.
    849         *
    850         * @throws ConversionException is thrown if the key maps to an
    851         *         object that is not a list of floats.
    852         */
    853        public List<Float> getFloatList(String key, List<Float> defaultValue)
    854        {
    855            return getList(Float.class, key, defaultValue);
    856        }
    857
    858        /**
    859         * Get an array of float primitives associated with the given
    860         * configuration key. If the key doesn't map to an existing object
    861         * an empty array is returned.
    862         *
    863         * @param key The configuration key.
    864         * @return The associated float array if the key is found.
    865         *
    866         * @throws ConversionException is thrown if the key maps to an
    867         *         object that is not a list of floats.
    868         */
    869        public float[] getFloatArray(String key)
    870        {
    871            return getFloatArray(key, new float[0]);
    872        }
    873
    874        /**
    875         * Get an array of float primitives associated with the given
    876         * configuration key. If the key doesn't map to an existing object
    877         * an empty array is returned.
    878         *
    879         * @param key The configuration key.
    880         * @param defaultValue the default value, which will be returned if the property is not found
    881         * @return The associated float array if the key is found.
    882         *
    883         * @throws ConversionException is thrown if the key maps to an
    884         *         object that is not a list of floats.
    885         */
    886        public float[] getFloatArray(String key, float[] defaultValue)
    887        {
    888            return (float[]) getArray(Float.TYPE, key, defaultValue);
    889        }
    890
    891        /**
    892         * Get a list of Double objects associated with the given
    893         * configuration key. If the key doesn't map to an existing object
    894         * an empty list is returned.
    895         *
    896         * @param key The configuration key.
    897         * @return The associated Double list if the key is found.
    898         *
    899         * @throws ConversionException is thrown if the key maps to an
    900         *         object that is not a list of doubles.
    901         */
    902        public List<Double> getDoubleList(String key)
    903        {
    904            return getDoubleList(key, new ArrayList<Double>());
    905        }
    906
    907        /**
    908         * Get a list of Double objects associated with the given
    909         * configuration key. If the key doesn't map to an existing object,
    910         * the default value is returned.
    911         *
    912         * @param key The configuration key.
    913         * @param defaultValue The default value.
    914         * @return The associated List of Doubles.
    915         *
    916         * @throws ConversionException is thrown if the key maps to an
    917         *         object that is not a list of doubles.
    918         */
    919        public List<Double> getDoubleList(String key, List<Double> defaultValue)
    920        {
    921            return getList(Double.class, key, defaultValue);
    922        }
    923
    924        /**
    925         * Get an array of double primitives associated with the given
    926         * configuration key. If the key doesn't map to an existing object
    927         * an empty array is returned.
    928         *
    929         * @param key The configuration key.
    930         * @return The associated double array if the key is found.
    931         *
    932         * @throws ConversionException is thrown if the key maps to an
    933         *         object that is not a list of doubles.
    934         */
    935        public double[] getDoubleArray(String key)
    936        {
    937            return getDoubleArray(key, new double[0]);
    938        }
    939
    940        /**
    941         * Get an array of double primitives associated with the given
    942         * configuration key. If the key doesn't map to an existing object
    943         * an empty array is returned.
    944         *
    945         * @param key The configuration key.
    946         * @param defaultValue the default value, which will be returned if the property is not found
    947         * @return The associated double array if the key is found.
    948         *
    949         * @throws ConversionException is thrown if the key maps to an
    950         *         object that is not a list of doubles.
    951         */
    952        public double[] getDoubleArray(String key, double[] defaultValue)
    953        {
    954            return (double[]) getArray(Double.TYPE, key, defaultValue);
    955        }
    956
    957        /**
    958         * Get a list of BigIntegers associated with the given configuration key.
    959         * If the key doesn't map to an existing object an empty list is returned.
    960         *
    961         * @param key The configuration key.
    962         * @return The associated BigInteger list if the key is found.
    963         *
    964         * @throws ConversionException is thrown if the key maps to an
    965         *         object that is not a list of BigIntegers.
    966         */
    967        public List<BigInteger> getBigIntegerList(String key)
    968        {
    969            return getBigIntegerList(key, new ArrayList<BigInteger>());
    970        }
    971
    972        /**
    973         * Get a list of BigIntegers associated with the given configuration key.
    974         * If the key doesn't map to an existing object, the default value is
    975         * returned.
    976         *
    977         * @param key The configuration key.
    978         * @param defaultValue The default value.
    979         * @return The associated List of BigIntegers.
    980         *
    981         * @throws ConversionException is thrown if the key maps to an
    982         *         object that is not a list of BigIntegers.
    983         */
    984        public List<BigInteger> getBigIntegerList(String key, List<BigInteger> defaultValue)
    985        {
    986            return getList(BigInteger.class, key, defaultValue);
    987        }
    988
    989        /**
    990         * Get an array of BigIntegers associated with the given
    991         * configuration key. If the key doesn't map to an existing object
    992         * an empty array is returned.
    993         *
    994         * @param key The configuration key.
    995         * @return The associated BigInteger array if the key is found.
    996         *
    997         * @throws ConversionException is thrown if the key maps to an
    998         *         object that is not a list of BigIntegers.
    999         */
    1000        public BigInteger[] getBigIntegerArray(String key)
    1001        {
    1002            return getBigIntegerArray(key, new BigInteger[0]);
    1003        }
    1004
    1005        /**
    1006         * Get an array of BigIntegers associated with the given
    1007         * configuration key. If the key doesn't map to an existing object
    1008         * an empty array is returned.
    1009         *
    1010         * @param key The configuration key.
    1011         * @param defaultValue the default value, which will be returned if the property is not found
    1012         * @return The associated BigInteger array if the key is found.
    1013         *
    1014         * @throws ConversionException is thrown if the key maps to an
    1015         *         object that is not a list of BigIntegers.
    1016         */
    1017        public BigInteger[] getBigIntegerArray(String key, BigInteger[] defaultValue)
    1018        {
    1019            return (BigInteger[]) getArray(BigInteger.class, key, defaultValue);
    1020        }
    1021
    1022        /**
    1023         * Get a list of BigDecimals associated with the given configuration key.
    1024         * If the key doesn't map to an existing object an empty list is returned.
    1025         *
    1026         * @param key The configuration key.
    1027         * @return The associated BigDecimal list if the key is found.
    1028         *
    1029         * @throws ConversionException is thrown if the key maps to an
    1030         *         object that is not a list of BigDecimals.
    1031         */
    1032        public List<BigDecimal> getBigDecimalList(String key)
    1033        {
    1034            return getBigDecimalList(key, new ArrayList<BigDecimal>());
    1035        }
    1036
    1037        /**
    1038         * Get a list of BigDecimals associated with the given configuration key.
    1039         * If the key doesn't map to an existing object, the default value is
    1040         * returned.
    1041         *
    1042         * @param key The configuration key.
    1043         * @param defaultValue The default value.
    1044         * @return The associated List of BigDecimals.
    1045         *
    1046         * @throws ConversionException is thrown if the key maps to an
    1047         *         object that is not a list of BigDecimals.
    1048         */
    1049        public List<BigDecimal> getBigDecimalList(String key, List<BigDecimal> defaultValue)
    1050        {
    1051            return getList(BigDecimal.class, key, defaultValue);
    1052        }
    1053
    1054        /**
    1055         * Get an array of BigDecimals associated with the given
    1056         * configuration key. If the key doesn't map to an existing object
    1057         * an empty array is returned.
    1058         *
    1059         * @param key The configuration key.
    1060         * @return The associated BigDecimal array if the key is found.
    1061         *
    1062         * @throws ConversionException is thrown if the key maps to an
    1063         *         object that is not a list of BigDecimals.
    1064         */
    1065        public BigDecimal[] getBigDecimalArray(String key)
    1066        {
    1067            return getBigDecimalArray(key, new BigDecimal[0]);
    1068        }
    1069
    1070        /**
    1071         * Get an array of BigDecimals associated with the given
    1072         * configuration key. If the key doesn't map to an existing object
    1073         * an empty array is returned.
    1074         *
    1075         * @param key The configuration key.
    1076         * @param defaultValue the default value, which will be returned if the property is not found
    1077         * @return The associated BigDecimal array if the key is found.
    1078         *
    1079         * @throws ConversionException is thrown if the key maps to an
    1080         *         object that is not a list of BigDecimals.
    1081         */
    1082        public BigDecimal[] getBigDecimalArray(String key, BigDecimal[] defaultValue)
    1083        {
    1084            return (BigDecimal[]) getArray(BigDecimal.class, key, defaultValue);
    1085        }
    1086
    1087        /**
    1088         * Get an URL associated with the given configuration key.
    1089         *
    1090         * @param key The configuration key.
    1091         * @return The associated URL.
    1092         *
    1093         * @throws ConversionException is thrown if the key maps to an
    1094         *         object that is not an URL.
    1095         */
    1096        public URL getURL(String key)
    1097        {
    1098            return get(URL.class, key);
    1099        }
    1100
    1101        /**
    1102         * Get an URL associated with the given configuration key.
    1103         * If the key doesn't map to an existing object, the default value
    1104         * is returned.
    1105         *
    1106         * @param key          The configuration key.
    1107         * @param defaultValue The default value.
    1108         * @return The associated URL.
    1109         *
    1110         * @throws ConversionException is thrown if the key maps to an
    1111         *         object that is not an URL.
    1112         */
    1113        public URL getURL(String key, URL defaultValue)
    1114        {
    1115            return get(URL.class, key, defaultValue);
    1116        }
    1117
    1118        /**
    1119         * Get a list of URLs associated with the given configuration key.
    1120         * If the key doesn't map to an existing object an empty list is returned.
    1121         *
    1122         * @param key The configuration key.
    1123         * @return The associated URL list if the key is found.
    1124         *
    1125         * @throws ConversionException is thrown if the key maps to an
    1126         *         object that is not a list of URLs.
    1127         */
    1128        public List<URL> getURLList(String key)
    1129        {
    1130            return getURLList(key, new ArrayList<URL>());
    1131        }
    1132
    1133        /**
    1134         * Get a list of URLs associated with the given configuration key.
    1135         * If the key doesn't map to an existing object, the default value is
    1136         * returned.
    1137         *
    1138         * @param key The configuration key.
    1139         * @param defaultValue The default value.
    1140         * @return The associated List of URLs.
    1141         *
    1142         * @throws ConversionException is thrown if the key maps to an
    1143         *         object that is not a list of URLs.
    1144         */
    1145        public List<URL> getURLList(String key, List<URL> defaultValue)
    1146        {
    1147            return getList(URL.class, key, defaultValue);
    1148        }
    1149
    1150        /**
    1151         * Get an array of URLs associated with the given configuration key.
    1152         * If the key doesn't map to an existing object an empty array is returned.
    1153         *
    1154         * @param key The configuration key.
    1155         * @return The associated URL array if the key is found.
    1156         *
    1157         * @throws ConversionException is thrown if the key maps to an
    1158         *         object that is not a list of URLs.
    1159         */
    1160        public URL[] getURLArray(String key)
    1161        {
    1162            return getURLArray(key, new URL[0]);
    1163        }
    1164
    1165        /**
    1166         * Get an array of URLs associated with the given configuration key.
    1167         * If the key doesn't map to an existing object an empty array is returned.
    1168         *
    1169         * @param key The configuration key.
    1170         * @param defaultValue the default value, which will be returned if the property is not found
    1171         * @return The associated URL array if the key is found.
    1172         *
    1173         * @throws ConversionException is thrown if the key maps to an
    1174         *         object that is not a list of URLs.
    1175         */
    1176        public URL[] getURLArray(String key, URL[] defaultValue)
    1177        {
    1178            return (URL[]) getArray(URL.class, key, defaultValue);
    1179        }
    1180
    1181        /**
    1182         * Get a Date associated with the given configuration key. If the property
    1183         * is a String, it will be parsed with the format defined by the user in
    1184         * the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    1185         * {@link #DEFAULT_DATE_FORMAT} pattern.
    1186         *
    1187         * @param key The configuration key.
    1188         * @return The associated Date.
    1189         *
    1190         * @throws ConversionException is thrown if the key maps to an
    1191         *         object that is not a Date.
    1192         */
    1193        public Date getDate(String key)
    1194        {
    1195            return get(Date.class, key);
    1196        }
    1197
    1198        /**
    1199         * Get a Date associated with the given configuration key. If the property
    1200         * is a String, it will be parsed with the specified format pattern.
    1201         *
    1202         * @param key    The configuration key.
    1203         * @param format The non-localized {@link java.text.DateFormat} pattern.
    1204         * @return The associated Date
    1205         *
    1206         * @throws ConversionException is thrown if the key maps to an
    1207         *         object that is not a Date.
    1208         */
    1209        public Date getDate(String key, String format)
    1210        {
    1211            Date value = getDate(key, null, format);
    1212            if (value != null)
    1213            {
    1214                return value;
    1215            }
    1216            else if (isThrowExceptionOnMissing())
    1217            {
    1218                throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    1219            }
    1220            else
    1221            {
    1222                return null;
    1223            }
    1224        }
    1225
    1226        /**
    1227         * Get a Date associated with the given configuration key. If the property
    1228         * is a String, it will be parsed with the format defined by the user in
    1229         * the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    1230         * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an
    1231         * existing object, the default value is returned.
    1232         *
    1233         * @param key          The configuration key.
    1234         * @param defaultValue The default value.
    1235         * @return The associated Date.
    1236         *
    1237         * @throws ConversionException is thrown if the key maps to an
    1238         *         object that is not a Date.
    1239         */
    1240        public Date getDate(String key, Date defaultValue)
    1241        {
    1242            return getDate(key, defaultValue, getDefaultDateFormat());
    1243        }
    1244
    1245        /**
    1246         * Get a Date associated with the given configuration key. If the property
    1247         * is a String, it will be parsed with the specified format pattern.
    1248         * If the key doesn't map to an existing object, the default value
    1249         * is returned.
    1250         *
    1251         * @param key          The configuration key.
    1252         * @param defaultValue The default value.
    1253         * @param format       The non-localized {@link java.text.DateFormat} pattern.
    1254         * @return The associated Date.
    1255         *
    1256         * @throws ConversionException is thrown if the key maps to an
    1257         *         object that is not a Date.
    1258         */
    1259        public Date getDate(String key, Date defaultValue, String format)
    1260        {
    1261            Object value = resolveContainerStore(key);
    1262
    1263            if (value == null)
    1264            {
    1265                return defaultValue;
    1266            }
    1267            else
    1268            {
    1269                try
    1270                {
    1271                    return PropertyConverter.toDate(interpolate(value), format);
    1272                }
    1273                catch (ConversionException e)
    1274                {
    1275                    throw new ConversionException('\'' + key + "' doesn't map to a Date", e);
    1276                }
    1277            }
    1278        }
    1279        public List<Date> getDateList(String key)
    1280        {
    1281            return getDateList(key, new ArrayList<Date>());
    1282        }
    1283
    1284        /**
    1285         * Get a list of Dates associated with the given configuration key.
    1286         * If the property is a list of Strings, they will be parsed with the
    1287         * specified format pattern. If the key doesn't map to an existing object
    1288         * an empty list is returned.
    1289         *
    1290         * @param key    The configuration key.
    1291         * @param format The non-localized {@link java.text.DateFormat} pattern.
    1292         * @return The associated Date list if the key is found.
    1293         *
    1294         * @throws ConversionException is thrown if the key maps to an
    1295         *         object that is not a list of Dates.
    1296         */
    1297        public List<Date> getDateList(String key, String format)
    1298        {
    1299            return getDateList(key, new ArrayList<Date>(), format);
    1300        }
    1301
    1302        /**
    1303         * Get a list of Dates associated with the given configuration key.
    1304         * If the property is a list of Strings, they will be parsed with the
    1305         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1306         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1307         * If the key doesn't map to an existing object, the default value is
    1308         * returned.
    1309         *
    1310         * @param key          The configuration key.
    1311         * @param defaultValue The default value.
    1312         * @return The associated Date list if the key is found.
    1313         *
    1314         * @throws ConversionException is thrown if the key maps to an
    1315         *         object that is not a list of Dates.
    1316         */
    1317        public List<Date> getDateList(String key, List<Date> defaultValue)
    1318        {
    1319            return getDateList(key, defaultValue, getDefaultDateFormat());
    1320        }
    1321
    1322        /**
    1323         * Get a list of Dates associated with the given configuration key.
    1324         * If the property is a list of Strings, they will be parsed with the
    1325         * specified format pattern. If the key doesn't map to an existing object,
    1326         * the default value is returned.
    1327         *
    1328         * @param key          The configuration key.
    1329         * @param defaultValue The default value.
    1330         * @param format       The non-localized {@link java.text.DateFormat} pattern.
    1331         * @return The associated Date list if the key is found.
    1332         *
    1333         * @throws ConversionException is thrown if the key maps to an
    1334         *         object that is not a list of Dates.
    1335         */
    1336        public List<Date> getDateList(String key, List<Date> defaultValue, String format)
    1337        {
    1338            Object value = getProperty(key);
    1339
    1340            List<Date> list;
    1341
    1342            if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
    1343            {
    1344                list = defaultValue;
    1345            }
    1346            else if (value.getClass().isArray())
    1347            {
    1348                list = new ArrayList<Date>();
    1349                int length = Array.getLength(value);
    1350                for (int i = 0; i < length; i++)
    1351                {
    1352                    list.add(convert(Date.class, key, interpolate(Array.get(value, i)), new String[] {format}));
    1353                }
    1354            }
    1355            else if (value instanceof Collection)
    1356            {
    1357                Collection<?> values = (Collection<?>) value;
    1358                list = new ArrayList<Date>();
    1359
    1360                for (Object o : values)
    1361                {
    1362                    list.add(convert(Date.class, key, interpolate(o), new String[] {format}));
    1363                }
    1364            }
    1365            else
    1366            {
    1367                // attempt to convert a single value
    1368                list = new ArrayList<Date>();
    1369                list.add(convert(Date.class, key, interpolate(value), new String[] {format}));
    1370            }
    1371
    1372            return list;
    1373        }
    1374
    1375        /**
    1376         * Get an array of Dates associated with the given configuration key.
    1377         * If the property is a list of Strings, they will be parsed with the
    1378         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1379         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1380         * If the key doesn't map to an existing object an empty array is returned.
    1381         *
    1382         * @param key The configuration key.
    1383         * @return The associated Date array if the key is found.
    1384         *
    1385         * @throws ConversionException is thrown if the key maps to an
    1386         *         object that is not a list of Dates.
    1387         */
    1388        public Date[] getDateArray(String key)
    1389        {
    1390            return getDateArray(key, new Date[0]);
    1391        }
    1392
    1393        /**
    1394         * Get an array of Dates associated with the given configuration key.
    1395         * If the property is a list of Strings, they will be parsed with the
    1396         * specified format pattern. If the key doesn't map to an existing object
    1397         * an empty array is returned.
    1398         *
    1399         * @param key    The configuration key.
    1400         * @param format The non-localized {@link java.text.DateFormat} pattern.
    1401         * @return The associated Date array if the key is found.
    1402         *
    1403         * @throws ConversionException is thrown if the key maps to an
    1404         *         object that is not a list of Dates.
    1405         */
    1406        public Date[] getDateArray(String key, String format)
    1407        {
    1408            return getDateArray(key, new Date[0], format);
    1409        }
    1410
    1411        /**
    1412         * Get an array of Dates associated with the given configuration key.
    1413         * If the property is a list of Strings, they will be parsed with the
    1414         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1415         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1416         * If the key doesn't map to an existing object an empty array is returned.
    1417         *
    1418         * @param key The configuration key.
    1419         * @param defaultValue the default value, which will be returned if the property is not found
    1420         * @return The associated Date array if the key is found.
    1421         *
    1422         * @throws ConversionException is thrown if the key maps to an
    1423         *         object that is not a list of Dates.
    1424         */
    1425        public Date[] getDateArray(String key, Date[] defaultValue)
    1426        {
    1427            return getDateArray(key, defaultValue, getDefaultDateFormat());
    1428        }
    1429
    1430        /**
    1431         * Get an array of Dates associated with the given configuration key.
    1432         * If the property is a list of Strings, they will be parsed with the
    1433         * specified format pattern. If the key doesn't map to an existing object,
    1434         * the default value is returned.
    1435         *
    1436         * @param key          The configuration key.
    1437         * @param defaultValue The default value.
    1438         * @param format       The non-localized {@link java.text.DateFormat} pattern.
    1439         * @return The associated Date array if the key is found.
    1440         *
    1441         * @throws ConversionException is thrown if the key maps to an
    1442         *         object that is not a list of Dates.
    1443         */
    1444        public Date[] getDateArray(String key, Date[] defaultValue, String format)
    1445        {
    1446            List<Date> list = getDateList(key, format);
    1447            if (list.isEmpty())
    1448            {
    1449                return defaultValue;
    1450            }
    1451            else
    1452            {
    1453                return list.toArray(new Date[list.size()]);
    1454            }
    1455        }
    1456
    1457        /**
    1458         * Get a Calendar associated with the given configuration key. If the
    1459         * property is a String, it will be parsed with the format defined by the
    1460         * user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
    1461         * with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1462         *
    1463         * @param key The configuration key.
    1464         * @return The associated Calendar.
    1465         *
    1466         * @throws ConversionException is thrown if the key maps to an
    1467         *         object that is not a Calendar.
    1468         */
    1469        public Calendar getCalendar(String key)
    1470        {
    1471            return get(Calendar.class, key);
    1472        }
    1473
    1474        /**
    1475         * Get a Calendar associated with the given configuration key. If the
    1476         * property is a String, it will be parsed with the specified format
    1477         * pattern.
    1478         *
    1479         * @param key    The configuration key.
    1480         * @param format The non-localized {@link java.text.DateFormat} pattern.
    1481         * @return The associated Calendar
    1482         *
    1483         * @throws ConversionException is thrown if the key maps to an
    1484         *         object that is not a Calendar.
    1485         */
    1486        public Calendar getCalendar(String key, String format)
    1487        {
    1488            Calendar value = getCalendar(key, null, format);
    1489            if (value != null)
    1490            {
    1491                return value;
    1492            }
    1493            else if (isThrowExceptionOnMissing())
    1494            {
    1495                throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    1496            }
    1497            else
    1498            {
    1499                return null;
    1500            }
    1501        }
    1502
    1503        /**
    1504         * Get a Calendar associated with the given configuration key. If the
    1505         * property is a String, it will be parsed with the format defined by the
    1506         * user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
    1507         * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map
    1508         * to an existing object, the default value is returned.
    1509         *
    1510         * @param key          The configuration key.
    1511         * @param defaultValue The default value.
    1512         * @return The associated Calendar.
    1513         *
    1514         * @throws ConversionException is thrown if the key maps to an
    1515         *         object that is not a Calendar.
    1516         */
    1517        public Calendar getCalendar(String key, Calendar defaultValue)
    1518        {
    1519            return getCalendar(key, defaultValue, getDefaultDateFormat());
    1520        }
    1521
    1522        /**
    1523         * Get a Calendar associated with the given configuration key. If the
    1524         * property is a String, it will be parsed with the specified format
    1525         * pattern. If the key doesn't map to an existing object, the default
    1526         * value is returned.
    1527         *
    1528         * @param key          The configuration key.
    1529         * @param defaultValue The default value.
    1530         * @param format       The non-localized {@link java.text.DateFormat} pattern.
    1531         * @return The associated Calendar.
    1532         *
    1533         * @throws ConversionException is thrown if the key maps to an
    1534         *         object that is not a Calendar.
    1535         */
    1536        public Calendar getCalendar(String key, Calendar defaultValue, String format)
    1537        {
    1538            Object value = resolveContainerStore(key);
    1539
    1540            if (value == null)
    1541            {
    1542                return defaultValue;
    1543            }
    1544            else
    1545            {
    1546                try
    1547                {
    1548                    return PropertyConverter.toCalendar(interpolate(value), format);
    1549                }
    1550                catch (ConversionException e)
    1551                {
    1552                    throw new ConversionException('\'' + key + "' doesn't map to a Calendar", e);
    1553                }
    1554            }
    1555        }
    1556
    1557        /**
    1558         * Get a list of Calendars associated with the given configuration key.
    1559         * If the property is a list of Strings, they will be parsed with the
    1560         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1561         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1562         * If the key doesn't map to an existing object an empty list is returned.
    1563         *
    1564         * @param key The configuration key.
    1565         * @return The associated Calendar list if the key is found.
    1566         *
    1567         * @throws ConversionException is thrown if the key maps to an
    1568         *         object that is not a list of Calendars.
    1569         */
    1570        public List<Calendar> getCalendarList(String key)
    1571        {
    1572            return getCalendarList(key, new ArrayList<Calendar>());
    1573        }
    1574
    1575        /**
    1576         * Get a list of Calendars associated with the given configuration key.
    1577         * If the property is a list of Strings, they will be parsed with the
    1578         * specified format pattern. If the key doesn't map to an existing object
    1579         * an empty list is returned.
    1580         *
    1581         * @param key    The configuration key.
    1582         * @param format The non-localized {@link java.text.DateFormat} pattern.
    1583         * @return The associated Calendar list if the key is found.
    1584         *
    1585         * @throws ConversionException is thrown if the key maps to an
    1586         *         object that is not a list of Calendars.
    1587         */
    1588        public List<Calendar> getCalendarList(String key, String format)
    1589        {
    1590            return getCalendarList(key, new ArrayList<Calendar>(), format);
    1591        }
    1592
    1593        /**
    1594         * Get a list of Calendars associated with the given configuration key.
    1595         * If the property is a list of Strings, they will be parsed with the
    1596         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1597         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1598         * If the key doesn't map to an existing object, the default value is
    1599         * returned.
    1600         *
    1601         * @param key The configuration key.
    1602         * @param defaultValue The default value.
    1603         * @return The associated Calendar list if the key is found.
    1604         *
    1605         * @throws ConversionException is thrown if the key maps to an
    1606         *         object that is not a list of Calendars.
    1607         */
    1608        public List<Calendar> getCalendarList(String key, List<Calendar> defaultValue)
    1609        {
    1610            return getCalendarList(key, defaultValue, getDefaultDateFormat());
    1611        }
    1612
    1613        /**
    1614         * Get a list of Calendars associated with the given configuration key.
    1615         * If the property is a list of Strings, they will be parsed with the
    1616         * specified format pattern. If the key doesn't map to an existing object,
    1617         * the default value is returned.
    1618         *
    1619         * @param key          The configuration key.
    1620         * @param defaultValue The default value.
    1621         * @param format       The non-localized {@link java.text.DateFormat} pattern.
    1622         * @return The associated Calendar list if the key is found.
    1623         *
    1624         * @throws ConversionException is thrown if the key maps to an
    1625         *         object that is not a list of Calendars.
    1626         */
    1627        public List<Calendar> getCalendarList(String key, List<Calendar> defaultValue, String format)
    1628        {
    1629            Object value = getProperty(key);
    1630
    1631            List<Calendar> list;
    1632
    1633            if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
    1634            {
    1635                list = defaultValue;
    1636            }
    1637            else if (value.getClass().isArray())
    1638            {
    1639                list = new ArrayList<Calendar>();
    1640                int length = Array.getLength(value);
    1641                for (int i = 0; i < length; i++)
    1642                {
    1643                    list.add(convert(Calendar.class, key, interpolate(Array.get(value, i)), new String[] {format}));
    1644                }
    1645            }
    1646            else if (value instanceof Collection)
    1647            {
    1648                Collection<?> values = (Collection<?>) value;
    1649                list = new ArrayList<Calendar>();
    1650
    1651                for (Object o : values)
    1652                {
    1653                    list.add(convert(Calendar.class, key, interpolate(o), new String[] {format}));
    1654                }
    1655            }
    1656            else
    1657            {
    1658                // attempt to convert a single value
    1659                list = new ArrayList<Calendar>();
    1660                list.add(convert(Calendar.class, key, interpolate(value), new String[] {format}));
    1661            }
    1662
    1663            return list;
    1664        }
    1665
    1666        /**
    1667         * Get an array of Calendars associated with the given configuration key.
    1668         * If the property is a list of Strings, they will be parsed with the
    1669         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1670         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1671         * If the key doesn't map to an existing object an empty array is returned.
    1672         *
    1673         * @param key The configuration key.
    1674         * @return The associated Calendar array if the key is found.
    1675         *
    1676         * @throws ConversionException is thrown if the key maps to an
    1677         *         object that is not a list of Calendars.
    1678         */
    1679        public Calendar[] getCalendarArray(String key)
    1680        {
    1681            return getCalendarArray(key, new Calendar[0]);
    1682        }
    1683
    1684        /**
    1685         * Get an array of Calendars associated with the given configuration key.
    1686         * If the property is a list of Strings, they will be parsed with the
    1687         * specified format pattern. If the key doesn't map to an existing object
    1688         * an empty array is returned.
    1689         *
    1690         * @param key    The configuration key.
    1691         * @param format The non-localized {@link java.text.DateFormat} pattern.
    1692         * @return The associated Calendar array if the key is found.
    1693         *
    1694         * @throws ConversionException is thrown if the key maps to an
    1695         *         object that is not a list of Calendars.
    1696         */
    1697        public Calendar[] getCalendarArray(String key, String format)
    1698        {
    1699            return getCalendarArray(key, new Calendar[0], format);
    1700        }
    1701
    1702        /**
    1703         * Get an array of Calendars associated with the given configuration key.
    1704         * If the property is a list of Strings, they will be parsed with the
    1705         * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
    1706         * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
    1707         * If the key doesn't map to an existing object an empty array is returned.
    1708         *
    1709         * @param key The configuration key.
    1710         * @param defaultValue the default value, which will be returned if the property is not found
    1711         * @return The associated Calendar array if the key is found.
    1712         *
    1713         * @throws ConversionException is thrown if the key maps to an
    1714         *         object that is not a list of Calendars.
    1715         */
    1716        public Calendar[] getCalendarArray(String key, Calendar[] defaultValue)
    1717        {
    1718            return getCalendarArray(key, defaultValue, getDefaultDateFormat());
    1719        }
    1720
    1721        /**
    1722         * Get an array of Calendars associated with the given configuration key.
    1723         * If the property is a list of Strings, they will be parsed with the
    1724         * specified format pattern. If the key doesn't map to an existing object,
    1725         * the default value is returned.
    1726         *
    1727         * @param key          The configuration key.
    1728         * @param defaultValue The default value.
    1729         * @param format       The non-localized {@link java.text.DateFormat} pattern.
    1730         * @return The associated Calendar array if the key is found.
    1731         *
    1732         * @throws ConversionException is thrown if the key maps to an
    1733         *         object that is not a list of Calendars.
    1734         */
    1735        public Calendar[] getCalendarArray(String key, Calendar[] defaultValue, String format)
    1736        {
    1737            List<Calendar> list = getCalendarList(key, format);
    1738            if (list.isEmpty())
    1739            {
    1740                return defaultValue;
    1741            }
    1742            else
    1743            {
    1744                return list.toArray(new Calendar[list.size()]);
    1745            }
    1746        }
    1747
    1748        /**
    1749         * Returns the date format specified by the user in the DATE_FORMAT_KEY
    1750         * property, or the default format otherwise.
    1751         *
    1752         * @return the default date format
    1753         */
    1754        private String getDefaultDateFormat()
    1755        {
    1756            return getString(DATE_FORMAT_KEY, DEFAULT_DATE_FORMAT);
    1757        }
    1758
    1759        /**
    1760         * Get a Locale associated with the given configuration key.
    1761         *
    1762         * @param key The configuration key.
    1763         * @return The associated Locale.
    1764         *
    1765         * @throws ConversionException is thrown if the key maps to an
    1766         *         object that is not a Locale.
    1767         */
    1768        public Locale getLocale(String key)
    1769        {
    1770            return get(Locale.class, key);
    1771        }
    1772
    1773        /**
    1774         * Get a Locale associated with the given configuration key.
    1775         * If the key doesn't map to an existing object, the default value
    1776         * is returned.
    1777         *
    1778         * @param key          The configuration key.
    1779         * @param defaultValue The default value.
    1780         * @return The associated Locale.
    1781         *
    1782         * @throws ConversionException is thrown if the key maps to an
    1783         *         object that is not a Locale.
    1784         */
    1785        public Locale getLocale(String key, Locale defaultValue)
    1786        {
    1787            return get(Locale.class, key, defaultValue);
    1788        }
    1789
    1790        /**
    1791         * Get a list of Locales associated with the given configuration key.
    1792         * If the key doesn't map to an existing object an empty list is returned.
    1793         *
    1794         * @param key The configuration key.
    1795         * @return The associated Locale list if the key is found.
    1796         *
    1797         * @throws ConversionException is thrown if the key maps to an
    1798         *         object that is not a list of Locales.
    1799         */
    1800        public List<Locale> getLocaleList(String key)
    1801        {
    1802            return getLocaleList(key, new ArrayList<Locale>());
    1803        }
    1804
    1805        /**
    1806         * Get a list of Locales associated with the given configuration key.
    1807         * If the key doesn't map to an existing object, the default value is
    1808         * returned.
    1809         *
    1810         * @param key The configuration key.
    1811         * @param defaultValue The default value.
    1812         * @return The associated List of Locales.
    1813         *
    1814         * @throws ConversionException is thrown if the key maps to an
    1815         *         object that is not a list of Locales.
    1816         */
    1817        public List<Locale> getLocaleList(String key, List<Locale> defaultValue)
    1818        {
    1819            return getList(Locale.class, key, defaultValue);
    1820        }
    1821
    1822        /**
    1823         * Get an array of Locales associated with the given
    1824         * configuration key. If the key doesn't map to an existing object
    1825         * an empty array is returned.
    1826         *
    1827         * @param key The configuration key.
    1828         * @return The associated Locale array if the key is found.
    1829         *
    1830         * @throws ConversionException is thrown if the key maps to an
    1831         *         object that is not a list of Locales.
    1832         */
    1833        public Locale[] getLocaleArray(String key)
    1834        {
    1835            return getLocaleArray(key, new Locale[0]);
    1836        }
    1837
    1838        /**
    1839         * Get an array of Locales associated with the given
    1840         * configuration key. If the key doesn't map to an existing object
    1841         * an empty array is returned.
    1842         *
    1843         * @param key The configuration key.
    1844         * @param defaultValue the default value, which will be returned if the property is not found
    1845         * @return The associated Locale array if the key is found.
    1846         *
    1847         * @throws ConversionException is thrown if the key maps to an
    1848         *         object that is not a list of Locales.
    1849         */
    1850        public Locale[] getLocaleArray(String key, Locale[] defaultValue)
    1851        {
    1852            return (Locale[]) getArray(Locale.class, key, defaultValue);
    1853        }
    1854
    1855        /**
    1856         * Get a Color associated with the given configuration key.
    1857         *
    1858         * @param key The configuration key.
    1859         * @return The associated Color.
    1860         *
    1861         * @throws ConversionException is thrown if the key maps to an
    1862         *         object that is not a Color.
    1863         */
    1864        public Color getColor(String key)
    1865        {
    1866            return get(Color.class, key);
    1867        }
    1868
    1869        /**
    1870         * Get a Color associated with the given configuration key.
    1871         * If the key doesn't map to an existing object, the default value
    1872         * is returned.
    1873         *
    1874         * @param key          The configuration key.
    1875         * @param defaultValue The default value.
    1876         * @return The associated Color.
    1877         *
    1878         * @throws ConversionException is thrown if the key maps to an
    1879         *         object that is not a Color.
    1880         */
    1881        public Color getColor(String key, Color defaultValue)
    1882        {
    1883            return get(Color.class, key, defaultValue);
    1884        }
    1885
    1886        /**
    1887         * Get a list of Colors associated with the given configuration key.
    1888         * If the key doesn't map to an existing object an empty list is returned.
    1889         *
    1890         * @param key The configuration key.
    1891         * @return The associated Color list if the key is found.
    1892         *
    1893         * @throws ConversionException is thrown if the key maps to an
    1894         *         object that is not a list of Colors.
    1895         */
    1896        public List<Color> getColorList(String key)
    1897        {
    1898            return getColorList(key, new ArrayList<Color>());
    1899        }
    1900
    1901        /**
    1902         * Get a list of Colors associated with the given configuration key.
    1903         * If the key doesn't map to an existing object, the default value is
    1904         * returned.
    1905         *
    1906         * @param key The configuration key.
    1907         * @param defaultValue The default value.
    1908         * @return The associated List of Colors.
    1909         *
    1910         * @throws ConversionException is thrown if the key maps to an
    1911         *         object that is not a list of Colors.
    1912         */
    1913        public List<Color> getColorList(String key, List<Color> defaultValue)
    1914        {
    1915            return getList(Color.class, key, defaultValue);
    1916        }
    1917
    1918        /**
    1919         * Get an array of Colors associated with the given
    1920         * configuration key. If the key doesn't map to an existing object
    1921         * an empty array is returned.
    1922         *
    1923         * @param key The configuration key.
    1924         * @return The associated Color array if the key is found.
    1925         *
    1926         * @throws ConversionException is thrown if the key maps to an
    1927         *         object that is not a list of Colors.
    1928         */
    1929        public Color[] getColorArray(String key)
    1930        {
    1931            return getColorArray(key, new Color[0]);
    1932        }
    1933
    1934        /**
    1935         * Get an array of Colors associated with the given
    1936         * configuration key. If the key doesn't map to an existing object
    1937         * an empty array is returned.
    1938         *
    1939         * @param key The configuration key.
    1940         * @param defaultValue the default value, which will be returned if the property is not found
    1941         * @return The associated Color array if the key is found.
    1942         *
    1943         * @throws ConversionException is thrown if the key maps to an
    1944         *         object that is not a list of Colors.
    1945         */
    1946        public Color[] getColorArray(String key, Color[] defaultValue)
    1947        {
    1948            return (Color[]) getArray(Color.class, key, defaultValue);
    1949        }
    1950
    1951        /**
    1952         * Helper method for performing a type conversion using the
    1953         * {@code PropertyConverter} class.
    1954         *
    1955         * @param <T> the target type of the conversion
    1956         * @param cls the target class of the conversion
    1957         * @param key the configuration key
    1958         * @param value the value to be converted
    1959         * @param params additional parameters
    1960         * @throws ConversionException if the value is not compatible with the
    1961         *         requested type
    1962         */
    1963        private static <T> T convert(Class<T> cls, String key, Object value,
    1964                Object[] params)
    1965        {
    1966            try
    1967            {
    1968                Object result = PropertyConverter.to(cls, value, params);
    1969                // Will not throw a ClassCastException because PropertyConverter
    1970                // would have thrown a ConversionException if conversion had failed.
    1971                return cls.cast(result);
    1972            }
    1973            catch (ConversionException e)
    1974            {
    1975                throw new ConversionException('\'' + key + "' doesn't map to a "
    1976                        + cls, e);
    1977            }
    1978        }
    1979    }