    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.configuration2;
    019
    020import java.awt.Color;
    021import java.math.BigDecimal;
    022import java.math.BigInteger;
    023import java.net.URI;
    024import java.net.URL;
    025import java.util.ArrayList;
    026import java.util.Calendar;
    027import java.util.Date;
    028import java.util.Iterator;
    029import java.util.List;
    030import java.util.Locale;
    031import java.util.NoSuchElementException;
    032
    033import org.apache.commons.configuration2.convert.ConversionHandler;
    034import org.apache.commons.configuration2.convert.DefaultConversionHandler;
    035import org.apache.commons.configuration2.ex.ConversionException;
    036import org.apache.commons.lang3.ArrayUtils;
    037import org.apache.commons.lang3.StringUtils;
    038
    039/**
    040 * Decorator providing additional getters for any Configuration. This extended 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>{@code javax.mail.internet.InternetAddress} (requires Javamail in the classpath)</li>
    049 * <li>{@code jakarta.mail.internet.InternetAddress} (requires Javamail 2.+ in the classpath)</li>
    050 * <li>{@link Enum}</li>
    051 * </ul>
    052 *
    053 * Lists and arrays are available for all types.<br>
    054 * Note that this class is only a thin wrapper over functionality already provided by {@link AbstractConfiguration}.
    055 * Basically, the generic {@code get()}, and {@code getCollection()} methods are used to actually perform data
    056 * conversions.
    057 *
    058 * <p>
    059 * <strong>Example</strong>
    060 * </p>
    061 *
    062 * Configuration file {@code config.properties}:
    063 *
    064 * <pre>
    065 * title.color = #0000FF
    066 * remote.host = 192.168.0.53
    067 * default.locales = fr,en,de
    068 * email.contact = dev@test.org, tester@test.org
    069 * </pre>
    070 *
    071 * Usage:
    072 *
    073 * <pre>
    074 * DataConfiguration config = new DataConfiguration(new PropertiesConfiguration("config.properties"));
    075 *
    076 * // retrieve a property using a specialized getter
    077 * Color color = config.getColor("title.color");
    078 *
    079 * // retrieve a property using a generic getter
    080 * InetAddress host = (InetAddress) config.get(InetAddress.class, "remote.host");
    081 * Locale[] locales = (Locale[]) config.getArray(Locale.class, "default.locales");
    082 * List contacts = config.getList(InternetAddress.class, "email.contact");
    083 * </pre>
    084 *
    085 * <p>
    086 * <strong>Dates</strong>
    087 * </p>
    088 *
    089 * Date objects are expected to be formatted with the pattern {@code yyyy-MM-dd HH:mm:ss}. This default format can be
    090 * changed by specifying another format in the getters, or by putting a date format in the configuration under the key
    091 * {@code org.apache.commons.configuration.format.date}. Alternatively, the date format can also be specified via the
    092 * {@code ConversionHandler} used by a configuration instance:
    093 *
    094 * <pre>
    095 * DefaultConversionHandler handler = new DefaultConversionHandler();
    096 * handler.setDateFormat("mm/dd/yyyy");
    097 * config.setConversionHandler(handler);
    098 * </pre>
    099 *
    100 * @since 1.1
    101 */
    102public class DataConfiguration extends AbstractConfiguration {
    103    /** The key of the property storing the user-defined date format. */
    104    public static final String DATE_FORMAT_KEY = "org.apache.commons.configuration.format.date";
    105
    106    /** The default format for dates. */
    107    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    108
    109    /** Empty array constant. */
    110    private static final URL[] EMPTY_URL_ARRAY = {};
    111
    112    /** Empty array constant. */
    113    private static final URI[] EMPTY_URI_ARRAY = {};
    114
    115    /** Empty array constant. */
    116    private static final Locale[] EMPTY_LOCALE_ARRAY = {};
    117
    118    /** Empty array constant. */
    119    private static final Date[] EMPTY_DATE_ARRAY = {};
    120
    121    /** Empty array constant. */
    122    private static final Color[] EMPTY_COLOR_ARRAY = {};
    123
    124    /** Empty array constant. */
    125    private static final Calendar[] EMPTY_CALENDARD_ARRAY = {};
    126
    127    /** Empty array constant. */
    128    private static final BigInteger[] EMPTY_BIG_INTEGER_ARRAY = {};
    129
    130    /** Empty array constant. */
    131    private static final BigDecimal[] EMPTY_BIG_DECIMAL_ARRAY = {};
    132
    133    /** Stores temporary date formats. */
    134    private static final ThreadLocal<String> TEMP_DATE_FORMAT = new ThreadLocal<>();
    135
    136    /** Stores the wrapped configuration. */
    137    private final Configuration configuration;
    138
    139    /** A special conversion handler object used by this configuration. */
    140    private final ConversionHandler dataConversionHandler;
    141
    142    /**
    143     * Creates a new instance of {@code DataConfiguration} and sets the wrapped configuration.
    144     *
    145     * @param configuration the wrapped configuration
    146     */
    147    public DataConfiguration(final Configuration configuration) {
    148        this.configuration = configuration;
    149        dataConversionHandler = new DataConversionHandler();
    150    }
    151
    152    /**
    153     * Gets the configuration decorated by this DataConfiguration.
    154     *
    155     * @return the wrapped configuration
    156     */
    157    public Configuration getConfiguration() {
    158        return configuration;
    159    }
    160
    161    /**
    162     * {@inheritDoc} This implementation returns the special conversion handler used by this configuration instance.
    163     */
    164    @Override
    165    public ConversionHandler getConversionHandler() {
    166        return dataConversionHandler;
    167    }
    168
    169    @Override
    170    protected Object getPropertyInternal(final String key) {
    171        return configuration.getProperty(key);
    172    }
    173
    174    @Override
    175    protected void addPropertyInternal(final String key, final Object obj) {
    176        configuration.addProperty(key, obj);
    177    }
    178
    179    @Override
    180    protected void addPropertyDirect(final String key, final Object value) {
    181        if (configuration instanceof AbstractConfiguration) {
    182            ((AbstractConfiguration) configuration).addPropertyDirect(key, value);
    183        } else {
    184            configuration.addProperty(key, value);
    185        }
    186    }
    187
    188    @Override
    189    protected boolean isEmptyInternal() {
    190        return configuration.isEmpty();
    191    }
    192
    193    @Override
    194    protected boolean containsKeyInternal(final String key) {
    195        return configuration.containsKey(key);
    196    }
    197
    198    @Override
    199    protected void clearPropertyDirect(final String key) {
    200        configuration.clearProperty(key);
    201    }
    202
    203    @Override
    204    protected void setPropertyInternal(final String key, final Object value) {
    205        configuration.setProperty(key, value);
    206    }
    207
    208    @Override
    209    protected Iterator<String> getKeysInternal() {
    210        return configuration.getKeys();
    211    }
    212
    213    /**
    214     * Gets a list of Boolean objects associated with the given configuration key. If the key doesn't map to an existing
    215     * object an empty list is returned.
    216     *
    217     * @param key The configuration key.
    218     * @return The associated Boolean list if the key is found.
    219     *
    220     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
    221     */
    222    public List<Boolean> getBooleanList(final String key) {
    223        return getBooleanList(key, new ArrayList<>());
    224    }
    225
    226    /**
    227     * Gets a list of Boolean objects associated with the given configuration key. If the key doesn't map to an existing
    228     * object, the default value is returned.
    229     *
    230     * @param key The configuration key.
    231     * @param defaultValue The default value.
    232     * @return The associated List of Booleans.
    233     *
    234     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
    235     */
    236    public List<Boolean> getBooleanList(final String key, final List<Boolean> defaultValue) {
    237        return getList(Boolean.class, key, defaultValue);
    238    }
    239
    240    /**
    241     * Gets an array of boolean primitives associated with the given configuration key. If the key doesn't map to an existing
    242     * object an empty array is returned.
    243     *
    244     * @param key The configuration key.
    245     * @return The associated boolean array if the key is found.
    246     *
    247     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
    248     */
    249    public boolean[] getBooleanArray(final String key) {
    250        return (boolean[]) getArray(Boolean.TYPE, key);
    251    }
    252
    253    /**
    254     * Gets an array of boolean primitives associated with the given configuration key. If the key doesn't map to an existing
    255     * object, the default value is returned.
    256     *
    257     * @param key The configuration key.
    258     * @param defaultValue The default value.
    259     * @return The associated boolean array if the key is found.
    260     *
    261     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
    262     */
    263    public boolean[] getBooleanArray(final String key, final boolean... defaultValue) {
    264        return get(boolean[].class, key, defaultValue);
    265    }
    266
    267    /**
    268     * Gets a list of Byte objects associated with the given configuration key. If the key doesn't map to an existing object
    269     * an empty list is returned.
    270     *
    271     * @param key The configuration key.
    272     * @return The associated Byte list if the key is found.
    273     *
    274     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
    275     */
    276    public List<Byte> getByteList(final String key) {
    277        return getByteList(key, new ArrayList<>());
    278    }
    279
    280    /**
    281     * Gets a list of Byte objects associated with the given configuration key. If the key doesn't map to an existing object,
    282     * the default value is returned.
    283     *
    284     * @param key The configuration key.
    285     * @param defaultValue The default value.
    286     * @return The associated List of Bytes.
    287     *
    288     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
    289     */
    290    public List<Byte> getByteList(final String key, final List<Byte> defaultValue) {
    291        return getList(Byte.class, key, defaultValue);
    292    }
    293
    294    /**
    295     * Gets an array of byte primitives associated with the given configuration key. If the key doesn't map to an existing
    296     * object an empty array is returned.
    297     *
    298     * @param key The configuration key.
    299     * @return The associated byte array if the key is found.
    300     *
    301     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
    302     */
    303    public byte[] getByteArray(final String key) {
    304        return getByteArray(key, ArrayUtils.EMPTY_BYTE_ARRAY);
    305    }
    306
    307    /**
    308     * Gets an array of byte primitives associated with the given configuration key. If the key doesn't map to an existing
    309     * object an empty array is returned.
    310     *
    311     * @param key The configuration key.
    312     * @param defaultValue the default value, which will be returned if the property is not found
    313     * @return The associated byte array if the key is found.
    314     *
    315     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
    316     */
    317    public byte[] getByteArray(final String key, final byte... defaultValue) {
    318        return get(byte[].class, key, defaultValue);
    319    }
    320
    321    /**
    322     * Gets a list of Short objects associated with the given configuration key. If the key doesn't map to an existing object
    323     * an empty list is returned.
    324     *
    325     * @param key The configuration key.
    326     * @return The associated Short list if the key is found.
    327     *
    328     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
    329     */
    330    public List<Short> getShortList(final String key) {
    331        return getShortList(key, new ArrayList<>());
    332    }
    333
    334    /**
    335     * Gets a list of Short objects associated with the given configuration key. If the key doesn't map to an existing
    336     * object, the default value is returned.
    337     *
    338     * @param key The configuration key.
    339     * @param defaultValue The default value.
    340     * @return The associated List of Shorts.
    341     *
    342     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
    343     */
    344    public List<Short> getShortList(final String key, final List<Short> defaultValue) {
    345        return getList(Short.class, key, defaultValue);
    346    }
    347
    348    /**
    349     * Gets an array of short primitives associated with the given configuration key. If the key doesn't map to an existing
    350     * object an empty array is returned.
    351     *
    352     * @param key The configuration key.
    353     * @return The associated short array if the key is found.
    354     *
    355     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
    356     */
    357    public short[] getShortArray(final String key) {
    358        return getShortArray(key, ArrayUtils.EMPTY_SHORT_ARRAY);
    359    }
    360
    361    /**
    362     * Gets an array of short primitives associated with the given configuration key. If the key doesn't map to an existing
    363     * object an empty array is returned.
    364     *
    365     * @param key The configuration key.
    366     * @param defaultValue the default value, which will be returned if the property is not found
    367     * @return The associated short array if the key is found.
    368     *
    369     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
    370     */
    371    public short[] getShortArray(final String key, final short... defaultValue) {
    372        return get(short[].class, key, defaultValue);
    373    }
    374
    375    /**
    376     * Gets a list of Integer objects associated with the given configuration key. If the key doesn't map to an existing
    377     * object an empty list is returned.
    378     *
    379     * @param key The configuration key.
    380     * @return The associated Integer list if the key is found.
    381     *
    382     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
    383     */
    384    public List<Integer> getIntegerList(final String key) {
    385        return getIntegerList(key, new ArrayList<>());
    386    }
    387
    388    /**
    389     * Gets a list of Integer objects associated with the given configuration key. If the key doesn't map to an existing
    390     * object, the default value is returned.
    391     *
    392     * @param key The configuration key.
    393     * @param defaultValue The default value.
    394     * @return The associated List of Integers.
    395     *
    396     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
    397     */
    398    public List<Integer> getIntegerList(final String key, final List<Integer> defaultValue) {
    399        return getList(Integer.class, key, defaultValue);
    400    }
    401
    402    /**
    403     * Gets an array of int primitives associated with the given configuration key. If the key doesn't map to an existing
    404     * object an empty array is returned.
    405     *
    406     * @param key The configuration key.
    407     * @return The associated int array if the key is found.
    408     *
    409     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
    410     */
    411    public int[] getIntArray(final String key) {
    412        return getIntArray(key, ArrayUtils.EMPTY_INT_ARRAY);
    413    }
    414
    415    /**
    416     * Gets an array of int primitives associated with the given configuration key. If the key doesn't map to an existing
    417     * object an empty array is returned.
    418     *
    419     * @param key The configuration key.
    420     * @param defaultValue the default value, which will be returned if the property is not found
    421     * @return The associated int array if the key is found.
    422     *
    423     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
    424     */
    425    public int[] getIntArray(final String key, final int... defaultValue) {
    426        return get(int[].class, key, defaultValue);
    427    }
    428
    429    /**
    430     * Gets a list of Long objects associated with the given configuration key. If the key doesn't map to an existing object
    431     * an empty list is returned.
    432     *
    433     * @param key The configuration key.
    434     * @return The associated Long list if the key is found.
    435     *
    436     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
    437     */
    438    public List<Long> getLongList(final String key) {
    439        return getLongList(key, new ArrayList<>());
    440    }
    441
    442    /**
    443     * Gets a list of Long objects associated with the given configuration key. If the key doesn't map to an existing object,
    444     * the default value is returned.
    445     *
    446     * @param key The configuration key.
    447     * @param defaultValue The default value.
    448     * @return The associated List of Longs.
    449     *
    450     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
    451     */
    452    public List<Long> getLongList(final String key, final List<Long> defaultValue) {
    453        return getList(Long.class, key, defaultValue);
    454    }
    455
    456    /**
    457     * Gets an array of long primitives associated with the given configuration key. If the key doesn't map to an existing
    458     * object an empty array is returned.
    459     *
    460     * @param key The configuration key.
    461     * @return The associated long array if the key is found.
    462     *
    463     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
    464     */
    465    public long[] getLongArray(final String key) {
    466        return getLongArray(key, ArrayUtils.EMPTY_LONG_ARRAY);
    467    }
    468
    469    /**
    470     * Gets an array of long primitives associated with the given configuration key. If the key doesn't map to an existing
    471     * object an empty array is returned.
    472     *
    473     * @param key The configuration key.
    474     * @param defaultValue the default value, which will be returned if the property is not found
    475     * @return The associated long array if the key is found.
    476     *
    477     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
    478     */
    479    public long[] getLongArray(final String key, final long... defaultValue) {
    480        return get(long[].class, key, defaultValue);
    481    }
    482
    483    /**
    484     * Gets a list of Float objects associated with the given configuration key. If the key doesn't map to an existing object
    485     * an empty list is returned.
    486     *
    487     * @param key The configuration key.
    488     * @return The associated Float list if the key is found.
    489     *
    490     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
    491     */
    492    public List<Float> getFloatList(final String key) {
    493        return getFloatList(key, new ArrayList<>());
    494    }
    495
    496    /**
    497     * Gets a list of Float objects associated with the given configuration key. If the key doesn't map to an existing
    498     * object, the default value is returned.
    499     *
    500     * @param key The configuration key.
    501     * @param defaultValue The default value.
    502     * @return The associated List of Floats.
    503     *
    504     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
    505     */
    506    public List<Float> getFloatList(final String key, final List<Float> defaultValue) {
    507        return getList(Float.class, key, defaultValue);
    508    }
    509
    510    /**
    511     * Gets an array of float primitives associated with the given configuration key. If the key doesn't map to an existing
    512     * object an empty array is returned.
    513     *
    514     * @param key The configuration key.
    515     * @return The associated float array if the key is found.
    516     *
    517     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
    518     */
    519    public float[] getFloatArray(final String key) {
    520        return getFloatArray(key, ArrayUtils.EMPTY_FLOAT_ARRAY);
    521    }
    522
    523    /**
    524     * Gets an array of float primitives associated with the given configuration key. If the key doesn't map to an existing
    525     * object an empty array is returned.
    526     *
    527     * @param key The configuration key.
    528     * @param defaultValue the default value, which will be returned if the property is not found
    529     * @return The associated float array if the key is found.
    530     *
    531     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
    532     */
    533    public float[] getFloatArray(final String key, final float... defaultValue) {
    534        return get(float[].class, key, defaultValue);
    535    }
    536
    537    /**
    538     * Gets a list of Double objects associated with the given configuration key. If the key doesn't map to an existing
    539     * object an empty list is returned.
    540     *
    541     * @param key The configuration key.
    542     * @return The associated Double list if the key is found.
    543     *
    544     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
    545     */
    546    public List<Double> getDoubleList(final String key) {
    547        return getDoubleList(key, new ArrayList<>());
    548    }
    549
    550    /**
    551     * Gets a list of Double objects associated with the given configuration key. If the key doesn't map to an existing
    552     * object, the default value is returned.
    553     *
    554     * @param key The configuration key.
    555     * @param defaultValue The default value.
    556     * @return The associated List of Doubles.
    557     *
    558     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
    559     */
    560    public List<Double> getDoubleList(final String key, final List<Double> defaultValue) {
    561        return getList(Double.class, key, defaultValue);
    562    }
    563
    564    /**
    565     * Gets an array of double primitives associated with the given configuration key. If the key doesn't map to an existing
    566     * object an empty array is returned.
    567     *
    568     * @param key The configuration key.
    569     * @return The associated double array if the key is found.
    570     *
    571     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
    572     */
    573    public double[] getDoubleArray(final String key) {
    574        return getDoubleArray(key, ArrayUtils.EMPTY_DOUBLE_ARRAY);
    575    }
    576
    577    /**
    578     * Gets an array of double primitives associated with the given configuration key. If the key doesn't map to an existing
    579     * object an empty array is returned.
    580     *
    581     * @param key The configuration key.
    582     * @param defaultValue the default value, which will be returned if the property is not found
    583     * @return The associated double array if the key is found.
    584     *
    585     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
    586     */
    587    public double[] getDoubleArray(final String key, final double... defaultValue) {
    588        return get(double[].class, key, defaultValue);
    589    }
    590
    591    /**
    592     * Gets a list of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
    593     * an empty list is returned.
    594     *
    595     * @param key The configuration key.
    596     * @return The associated BigInteger list if the key is found.
    597     *
    598     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
    599     */
    600    public List<BigInteger> getBigIntegerList(final String key) {
    601        return getBigIntegerList(key, new ArrayList<>());
    602    }
    603
    604    /**
    605     * Gets a list of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object,
    606     * the default value is returned.
    607     *
    608     * @param key The configuration key.
    609     * @param defaultValue The default value.
    610     * @return The associated List of BigIntegers.
    611     *
    612     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
    613     */
    614    public List<BigInteger> getBigIntegerList(final String key, final List<BigInteger> defaultValue) {
    615        return getList(BigInteger.class, key, defaultValue);
    616    }
    617
    618    /**
    619     * Gets an array of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
    620     * an empty array is returned.
    621     *
    622     * @param key The configuration key.
    623     * @return The associated BigInteger array if the key is found.
    624     *
    625     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
    626     */
    627    public BigInteger[] getBigIntegerArray(final String key) {
    628        return getBigIntegerArray(key, EMPTY_BIG_INTEGER_ARRAY);
    629    }
    630
    631    /**
    632     * Gets an array of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
    633     * an empty array is returned.
    634     *
    635     * @param key The configuration key.
    636     * @param defaultValue the default value, which will be returned if the property is not found
    637     * @return The associated BigInteger array if the key is found.
    638     *
    639     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
    640     */
    641    public BigInteger[] getBigIntegerArray(final String key, final BigInteger... defaultValue) {
    642        return get(BigInteger[].class, key, defaultValue);
    643    }
    644
    645    /**
    646     * Gets a list of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
    647     * an empty list is returned.
    648     *
    649     * @param key The configuration key.
    650     * @return The associated BigDecimal list if the key is found.
    651     *
    652     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
    653     */
    654    public List<BigDecimal> getBigDecimalList(final String key) {
    655        return getBigDecimalList(key, new ArrayList<>());
    656    }
    657
    658    /**
    659     * Gets a list of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object,
    660     * the default value is returned.
    661     *
    662     * @param key The configuration key.
    663     * @param defaultValue The default value.
    664     * @return The associated List of BigDecimals.
    665     *
    666     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
    667     */
    668    public List<BigDecimal> getBigDecimalList(final String key, final List<BigDecimal> defaultValue) {
    669        return getList(BigDecimal.class, key, defaultValue);
    670    }
    671
    672    /**
    673     * Gets an array of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
    674     * an empty array is returned.
    675     *
    676     * @param key The configuration key.
    677     * @return The associated BigDecimal array if the key is found.
    678     *
    679     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
    680     */
    681    public BigDecimal[] getBigDecimalArray(final String key) {
    682        return getBigDecimalArray(key, EMPTY_BIG_DECIMAL_ARRAY);
    683    }
    684
    685    /**
    686     * Gets an array of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
    687     * an empty array is returned.
    688     *
    689     * @param key The configuration key.
    690     * @param defaultValue the default value, which will be returned if the property is not found
    691     * @return The associated BigDecimal array if the key is found.
    692     *
    693     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
    694     */
    695    public BigDecimal[] getBigDecimalArray(final String key, final BigDecimal... defaultValue) {
    696        return get(BigDecimal[].class, key, defaultValue);
    697    }
    698
    699    /**
    700     * Gets an URI associated with the given configuration key.
    701     *
    702     * @param key The configuration key.
    703     * @return The associated URI.
    704     *
    705     * @throws ConversionException is thrown if the key maps to an object that is not an URI.
    706     */
    707    public URI getURI(final String key) {
    708        return get(URI.class, key);
    709    }
    710
    711    /**
    712     * Gets an URI associated with the given configuration key. If the key doesn't map to an existing object, the default
    713     * value is returned.
    714     *
    715     * @param key The configuration key.
    716     * @param defaultValue The default value.
    717     * @return The associated URI.
    718     *
    719     * @throws ConversionException is thrown if the key maps to an object that is not an URI.
    720     */
    721    public URI getURI(final String key, final URI defaultValue) {
    722        return get(URI.class, key, defaultValue);
    723    }
    724
    725    /**
    726     * Gets an array of URIs associated with the given configuration key. If the key doesn't map to an existing object an
    727     * empty array is returned.
    728     *
    729     * @param key The configuration key.
    730     * @return The associated URI array if the key is found.
    731     *
    732     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
    733     */
    734    public URI[] getURIArray(final String key) {
    735        return getURIArray(key, EMPTY_URI_ARRAY);
    736    }
    737
    738    /**
    739     * Gets an array of URIs associated with the given configuration key. If the key doesn't map to an existing object an
    740     * empty array is returned.
    741     *
    742     * @param key The configuration key.
    743     * @param defaultValue the default value, which will be returned if the property is not found
    744     * @return The associated URI array if the key is found.
    745     *
    746     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
    747     */
    748    public URI[] getURIArray(final String key, final URI... defaultValue) {
    749        return get(URI[].class, key, defaultValue);
    750    }
    751
    752    /**
    753     * Gets a list of URIs associated with the given configuration key. If the key doesn't map to an existing object an empty
    754     * list is returned.
    755     *
    756     * @param key The configuration key.
    757     * @return The associated URI list if the key is found.
    758     *
    759     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
    760     */
    761    public List<URI> getURIList(final String key) {
    762        return getURIList(key, new ArrayList<>());
    763    }
    764
    765    /**
    766     * Gets a list of URIs associated with the given configuration key. If the key doesn't map to an existing object, the
    767     * default value is returned.
    768     *
    769     * @param key The configuration key.
    770     * @param defaultValue The default value.
    771     * @return The associated List of URIs.
    772     *
    773     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
    774     */
    775    public List<URI> getURIList(final String key, final List<URI> defaultValue) {
    776        return getList(URI.class, key, defaultValue);
    777    }
    778
    779    /**
    780     * Gets an URL associated with the given configuration key.
    781     *
    782     * @param key The configuration key.
    783     * @return The associated URL.
    784     *
    785     * @throws ConversionException is thrown if the key maps to an object that is not an URL.
    786     */
    787    public URL getURL(final String key) {
    788        return get(URL.class, key);
    789    }
    790
    791    /**
    792     * Gets an URL associated with the given configuration key. If the key doesn't map to an existing object, the default
    793     * value is returned.
    794     *
    795     * @param key The configuration key.
    796     * @param defaultValue The default value.
    797     * @return The associated URL.
    798     *
    799     * @throws ConversionException is thrown if the key maps to an object that is not an URL.
    800     */
    801    public URL getURL(final String key, final URL defaultValue) {
    802        return get(URL.class, key, defaultValue);
    803    }
    804
    805    /**
    806     * Gets a list of URLs associated with the given configuration key. If the key doesn't map to an existing object an empty
    807     * list is returned.
    808     *
    809     * @param key The configuration key.
    810     * @return The associated URL list if the key is found.
    811     *
    812     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
    813     */
    814    public List<URL> getURLList(final String key) {
    815        return getURLList(key, new ArrayList<>());
    816    }
    817
    818    /**
    819     * Gets a list of URLs associated with the given configuration key. If the key doesn't map to an existing object, the
    820     * default value is returned.
    821     *
    822     * @param key The configuration key.
    823     * @param defaultValue The default value.
    824     * @return The associated List of URLs.
    825     *
    826     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
    827     */
    828    public List<URL> getURLList(final String key, final List<URL> defaultValue) {
    829        return getList(URL.class, key, defaultValue);
    830    }
    831
    832    /**
    833     * Gets an array of URLs associated with the given configuration key. If the key doesn't map to an existing object an
    834     * empty array is returned.
    835     *
    836     * @param key The configuration key.
    837     * @return The associated URL array if the key is found.
    838     *
    839     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
    840     */
    841    public URL[] getURLArray(final String key) {
    842        return getURLArray(key, EMPTY_URL_ARRAY);
    843    }
    844
    845    /**
    846     * Gets an array of URLs associated with the given configuration key. If the key doesn't map to an existing object an
    847     * empty array is returned.
    848     *
    849     * @param key The configuration key.
    850     * @param defaultValue the default value, which will be returned if the property is not found
    851     * @return The associated URL array if the key is found.
    852     *
    853     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
    854     */
    855    public URL[] getURLArray(final String key, final URL... defaultValue) {
    856        return get(URL[].class, key, defaultValue);
    857    }
    858
    859    /**
    860     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
    861     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    862     * {@link #DEFAULT_DATE_FORMAT} pattern.
    863     *
    864     * @param key The configuration key.
    865     * @return The associated Date.
    866     *
    867     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
    868     */
    869    public Date getDate(final String key) {
    870        return get(Date.class, key);
    871    }
    872
    873    /**
    874     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
    875     * specified format pattern.
    876     *
    877     * @param key The configuration key.
    878     * @param format The non-localized {@link java.text.DateFormat} pattern.
    879     * @return The associated Date
    880     *
    881     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
    882     */
    883    public Date getDate(final String key, final String format) {
    884        final Date value = getDate(key, null, format);
    885        if (value != null) {
    886            return value;
    887        }
    888        if (isThrowExceptionOnMissing()) {
    889            throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    890        }
    891        return null;
    892    }
    893
    894    /**
    895     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
    896     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    897     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
    898     *
    899     * @param key The configuration key.
    900     * @param defaultValue The default value.
    901     * @return The associated Date.
    902     *
    903     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
    904     */
    905    public Date getDate(final String key, final Date defaultValue) {
    906        return getDate(key, defaultValue, null);
    907    }
    908
    909    /**
    910     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
    911     * specified format pattern. If the key doesn't map to an existing object, the default value is returned.
    912     *
    913     * @param key The configuration key.
    914     * @param defaultValue The default value.
    915     * @param format The non-localized {@link java.text.DateFormat} pattern.
    916     * @return The associated Date.
    917     *
    918     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
    919     */
    920    public Date getDate(final String key, final Date defaultValue, final String format) {
    921        TEMP_DATE_FORMAT.set(format);
    922        try {
    923            return get(Date.class, key, defaultValue);
    924        } finally {
    925            TEMP_DATE_FORMAT.remove();
    926        }
    927    }
    928
    929    public List<Date> getDateList(final String key) {
    930        return getDateList(key, new ArrayList<>());
    931    }
    932
    933    /**
    934     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    935     * parsed with the specified format pattern. If the key doesn't map to an existing object an empty list is returned.
    936     *
    937     * @param key The configuration key.
    938     * @param format The non-localized {@link java.text.DateFormat} pattern.
    939     * @return The associated Date list if the key is found.
    940     *
    941     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    942     */
    943    public List<Date> getDateList(final String key, final String format) {
    944        return getDateList(key, new ArrayList<>(), format);
    945    }
    946
    947    /**
    948     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    949     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    950     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
    951     *
    952     * @param key The configuration key.
    953     * @param defaultValue The default value.
    954     * @return The associated Date list if the key is found.
    955     *
    956     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    957     */
    958    public List<Date> getDateList(final String key, final List<Date> defaultValue) {
    959        return getDateList(key, defaultValue, null);
    960    }
    961
    962    /**
    963     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    964     * parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
    965     * returned.
    966     *
    967     * @param key The configuration key.
    968     * @param defaultValue The default value.
    969     * @param format The non-localized {@link java.text.DateFormat} pattern.
    970     * @return The associated Date list if the key is found.
    971     *
    972     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    973     */
    974    public List<Date> getDateList(final String key, final List<Date> defaultValue, final String format) {
    975        TEMP_DATE_FORMAT.set(format);
    976        try {
    977            return getList(Date.class, key, defaultValue);
    978        } finally {
    979            TEMP_DATE_FORMAT.remove();
    980        }
    981    }
    982
    983    /**
    984     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    985     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    986     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is returned.
    987     *
    988     * @param key The configuration key.
    989     * @return The associated Date array if the key is found.
    990     *
    991     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    992     */
    993    public Date[] getDateArray(final String key) {
    994        return getDateArray(key, EMPTY_DATE_ARRAY);
    995    }
    996
    997    /**
    998     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    999     * parsed with the specified format pattern. If the key doesn't map to an existing object an empty array is returned.
    1000     *
    1001     * @param key The configuration key.
    1002     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1003     * @return The associated Date array if the key is found.
    1004     *
    1005     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    1006     */
    1007    public Date[] getDateArray(final String key, final String format) {
    1008        return getDateArray(key, EMPTY_DATE_ARRAY, format);
    1009    }
    1010
    1011    /**
    1012     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    1013     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    1014     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is returned.
    1015     *
    1016     * @param key The configuration key.
    1017     * @param defaultValue the default value, which will be returned if the property is not found
    1018     * @return The associated Date array if the key is found.
    1019     *
    1020     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    1021     */
    1022    public Date[] getDateArray(final String key, final Date... defaultValue) {
    1023        return getDateArray(key, defaultValue, null);
    1024    }
    1025
    1026    /**
    1027     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
    1028     * parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
    1029     * returned.
    1030     *
    1031     * @param key The configuration key.
    1032     * @param defaultValue The default value.
    1033     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1034     * @return The associated Date array if the key is found.
    1035     *
    1036     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
    1037     */
    1038    public Date[] getDateArray(final String key, final Date[] defaultValue, final String format) {
    1039        TEMP_DATE_FORMAT.set(format);
    1040        try {
    1041            return get(Date[].class, key, defaultValue);
    1042        } finally {
    1043            TEMP_DATE_FORMAT.remove();
    1044        }
    1045    }
    1046
    1047    /**
    1048     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
    1049     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    1050     * {@link #DEFAULT_DATE_FORMAT} pattern.
    1051     *
    1052     * @param key The configuration key.
    1053     * @return The associated Calendar.
    1054     *
    1055     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
    1056     */
    1057    public Calendar getCalendar(final String key) {
    1058        return get(Calendar.class, key);
    1059    }
    1060
    1061    /**
    1062     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
    1063     * specified format pattern.
    1064     *
    1065     * @param key The configuration key.
    1066     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1067     * @return The associated Calendar
    1068     *
    1069     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
    1070     */
    1071    public Calendar getCalendar(final String key, final String format) {
    1072        final Calendar value = getCalendar(key, null, format);
    1073        if (value != null) {
    1074            return value;
    1075        }
    1076        if (isThrowExceptionOnMissing()) {
    1077            throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
    1078        }
    1079        return null;
    1080    }
    1081
    1082    /**
    1083     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
    1084     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
    1085     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
    1086     *
    1087     * @param key The configuration key.
    1088     * @param defaultValue The default value.
    1089     * @return The associated Calendar.
    1090     *
    1091     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
    1092     */
    1093    public Calendar getCalendar(final String key, final Calendar defaultValue) {
    1094        return getCalendar(key, defaultValue, null);
    1095    }
    1096
    1097    /**
    1098     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
    1099     * specified format pattern. If the key doesn't map to an existing object, the default value is returned.
    1100     *
    1101     * @param key The configuration key.
    1102     * @param defaultValue The default value.
    1103     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1104     * @return The associated Calendar.
    1105     *
    1106     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
    1107     */
    1108    public Calendar getCalendar(final String key, final Calendar defaultValue, final String format) {
    1109        TEMP_DATE_FORMAT.set(format);
    1110        try {
    1111            return get(Calendar.class, key, defaultValue);
    1112        } finally {
    1113            TEMP_DATE_FORMAT.remove();
    1114        }
    1115    }
    1116
    1117    /**
    1118     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
    1119     * be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with
    1120     * the {@link #DEFAULT_DATE_FORMAT} pattern. 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 Calendar list if the key is found.
    1124     *
    1125     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1126     */
    1127    public List<Calendar> getCalendarList(final String key) {
    1128        return getCalendarList(key, new ArrayList<>());
    1129    }
    1130
    1131    /**
    1132     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
    1133     * be parsed with the specified format pattern. If the key doesn't map to an existing object an empty list is returned.
    1134     *
    1135     * @param key The configuration key.
    1136     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1137     * @return The associated Calendar list if the key is found.
    1138     *
    1139     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1140     */
    1141    public List<Calendar> getCalendarList(final String key, final String format) {
    1142        return getCalendarList(key, new ArrayList<>(), format);
    1143    }
    1144
    1145    /**
    1146     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
    1147     * be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with
    1148     * the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is
    1149     * returned.
    1150     *
    1151     * @param key The configuration key.
    1152     * @param defaultValue The default value.
    1153     * @return The associated Calendar list if the key is found.
    1154     *
    1155     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1156     */
    1157    public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue) {
    1158        return getCalendarList(key, defaultValue, null);
    1159    }
    1160
    1161    /**
    1162     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
    1163     * be parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
    1164     * returned.
    1165     *
    1166     * @param key The configuration key.
    1167     * @param defaultValue The default value.
    1168     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1169     * @return The associated Calendar list if the key is found.
    1170     *
    1171     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1172     */
    1173    public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue, final String format) {
    1174        TEMP_DATE_FORMAT.set(format);
    1175        try {
    1176            return getList(Calendar.class, key, defaultValue);
    1177        } finally {
    1178            TEMP_DATE_FORMAT.remove();
    1179        }
    1180    }
    1181
    1182    /**
    1183     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
    1184     * will be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
    1185     * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is
    1186     * returned.
    1187     *
    1188     * @param key The configuration key.
    1189     * @return The associated Calendar array if the key is found.
    1190     *
    1191     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1192     */
    1193    public Calendar[] getCalendarArray(final String key) {
    1194        return getCalendarArray(key, EMPTY_CALENDARD_ARRAY);
    1195    }
    1196
    1197    /**
    1198     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
    1199     * will be parsed with the specified format pattern. If the key doesn't map to an existing object an empty array is
    1200     * returned.
    1201     *
    1202     * @param key The configuration key.
    1203     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1204     * @return The associated Calendar array if the key is found.
    1205     *
    1206     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1207     */
    1208    public Calendar[] getCalendarArray(final String key, final String format) {
    1209        return getCalendarArray(key, EMPTY_CALENDARD_ARRAY, format);
    1210    }
    1211
    1212    /**
    1213     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
    1214     * will be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
    1215     * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is
    1216     * returned.
    1217     *
    1218     * @param key The configuration key.
    1219     * @param defaultValue the default value, which will be returned if the property is not found
    1220     * @return The associated Calendar array if the key is found.
    1221     *
    1222     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1223     */
    1224    public Calendar[] getCalendarArray(final String key, final Calendar... defaultValue) {
    1225        return getCalendarArray(key, defaultValue, null);
    1226    }
    1227
    1228    /**
    1229     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
    1230     * will be parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
    1231     * returned.
    1232     *
    1233     * @param key The configuration key.
    1234     * @param defaultValue The default value.
    1235     * @param format The non-localized {@link java.text.DateFormat} pattern.
    1236     * @return The associated Calendar array if the key is found.
    1237     *
    1238     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
    1239     */
    1240    public Calendar[] getCalendarArray(final String key, final Calendar[] defaultValue, final String format) {
    1241        TEMP_DATE_FORMAT.set(format);
    1242        try {
    1243            return get(Calendar[].class, key, defaultValue);
    1244        } finally {
    1245            TEMP_DATE_FORMAT.remove();
    1246        }
    1247    }
    1248
    1249    /**
    1250     * Gets the date format specified by the user in the DATE_FORMAT_KEY property, or the default format otherwise.
    1251     *
    1252     * @return the default date format
    1253     */
    1254    private String getDefaultDateFormat() {
    1255        return getString(DATE_FORMAT_KEY, DEFAULT_DATE_FORMAT);
    1256    }
    1257
    1258    /**
    1259     * Gets a Locale associated with the given configuration key.
    1260     *
    1261     * @param key The configuration key.
    1262     * @return The associated Locale.
    1263     *
    1264     * @throws ConversionException is thrown if the key maps to an object that is not a Locale.
    1265     */
    1266    public Locale getLocale(final String key) {
    1267        return get(Locale.class, key);
    1268    }
    1269
    1270    /**
    1271     * Gets a Locale associated with the given configuration key. If the key doesn't map to an existing object, the default
    1272     * value is returned.
    1273     *
    1274     * @param key The configuration key.
    1275     * @param defaultValue The default value.
    1276     * @return The associated Locale.
    1277     *
    1278     * @throws ConversionException is thrown if the key maps to an object that is not a Locale.
    1279     */
    1280    public Locale getLocale(final String key, final Locale defaultValue) {
    1281        return get(Locale.class, key, defaultValue);
    1282    }
    1283
    1284    /**
    1285     * Gets a list of Locales associated with the given configuration key. If the key doesn't map to an existing object an
    1286     * empty list is returned.
    1287     *
    1288     * @param key The configuration key.
    1289     * @return The associated Locale list if the key is found.
    1290     *
    1291     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
    1292     */
    1293    public List<Locale> getLocaleList(final String key) {
    1294        return getLocaleList(key, new ArrayList<>());
    1295    }
    1296
    1297    /**
    1298     * Gets a list of Locales associated with the given configuration key. If the key doesn't map to an existing object, the
    1299     * default value is returned.
    1300     *
    1301     * @param key The configuration key.
    1302     * @param defaultValue The default value.
    1303     * @return The associated List of Locales.
    1304     *
    1305     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
    1306     */
    1307    public List<Locale> getLocaleList(final String key, final List<Locale> defaultValue) {
    1308        return getList(Locale.class, key, defaultValue);
    1309    }
    1310
    1311    /**
    1312     * Gets an array of Locales associated with the given configuration key. If the key doesn't map to an existing object an
    1313     * empty array is returned.
    1314     *
    1315     * @param key The configuration key.
    1316     * @return The associated Locale array if the key is found.
    1317     *
    1318     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
    1319     */
    1320    public Locale[] getLocaleArray(final String key) {
    1321        return getLocaleArray(key, EMPTY_LOCALE_ARRAY);
    1322    }
    1323
    1324    /**
    1325     * Gets an array of Locales associated with the given configuration key. If the key doesn't map to an existing object an
    1326     * empty array is returned.
    1327     *
    1328     * @param key The configuration key.
    1329     * @param defaultValue the default value, which will be returned if the property is not found
    1330     * @return The associated Locale array if the key is found.
    1331     *
    1332     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
    1333     */
    1334    public Locale[] getLocaleArray(final String key, final Locale... defaultValue) {
    1335        return get(Locale[].class, key, defaultValue);
    1336    }
    1337
    1338    /**
    1339     * Gets a Color associated with the given configuration key.
    1340     *
    1341     * @param key The configuration key.
    1342     * @return The associated Color.
    1343     *
    1344     * @throws ConversionException is thrown if the key maps to an object that is not a Color.
    1345     */
    1346    public Color getColor(final String key) {
    1347        return get(Color.class, key);
    1348    }
    1349
    1350    /**
    1351     * Gets a Color associated with the given configuration key. If the key doesn't map to an existing object, the default
    1352     * value is returned.
    1353     *
    1354     * @param key The configuration key.
    1355     * @param defaultValue The default value.
    1356     * @return The associated Color.
    1357     *
    1358     * @throws ConversionException is thrown if the key maps to an object that is not a Color.
    1359     */
    1360    public Color getColor(final String key, final Color defaultValue) {
    1361        return get(Color.class, key, defaultValue);
    1362    }
    1363
    1364    /**
    1365     * Gets a list of Colors associated with the given configuration key. If the key doesn't map to an existing object an
    1366     * empty list is returned.
    1367     *
    1368     * @param key The configuration key.
    1369     * @return The associated Color list if the key is found.
    1370     *
    1371     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
    1372     */
    1373    public List<Color> getColorList(final String key) {
    1374        return getColorList(key, new ArrayList<>());
    1375    }
    1376
    1377    /**
    1378     * Gets a list of Colors associated with the given configuration key. If the key doesn't map to an existing object, the
    1379     * default value is returned.
    1380     *
    1381     * @param key The configuration key.
    1382     * @param defaultValue The default value.
    1383     * @return The associated List of Colors.
    1384     *
    1385     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
    1386     */
    1387    public List<Color> getColorList(final String key, final List<Color> defaultValue) {
    1388        return getList(Color.class, key, defaultValue);
    1389    }
    1390
    1391    /**
    1392     * Gets an array of Colors associated with the given configuration key. If the key doesn't map to an existing object an
    1393     * empty array is returned.
    1394     *
    1395     * @param key The configuration key.
    1396     * @return The associated Color array if the key is found.
    1397     *
    1398     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
    1399     */
    1400    public Color[] getColorArray(final String key) {
    1401        return getColorArray(key, EMPTY_COLOR_ARRAY);
    1402    }
    1403
    1404    /**
    1405     * Gets an array of Colors associated with the given configuration key. If the key doesn't map to an existing object an
    1406     * empty array is returned.
    1407     *
    1408     * @param key The configuration key.
    1409     * @param defaultValue the default value, which will be returned if the property is not found
    1410     * @return The associated Color array if the key is found.
    1411     *
    1412     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
    1413     */
    1414    public Color[] getColorArray(final String key, final Color... defaultValue) {
    1415        return get(Color[].class, key, defaultValue);
    1416    }
    1417
    1418    /**
    1419     * Gets the original conversion handler set for this configuration. If this is not a
    1420     * {@code DefaultConversionHandler}, result is <b>null</b>.
    1421     *
    1422     * @return the original conversion handler or <b>null</b>
    1423     */
    1424    private DefaultConversionHandler getOriginalConversionHandler() {
    1425        final ConversionHandler handler = super.getConversionHandler();
    1426        return (DefaultConversionHandler) (handler instanceof DefaultConversionHandler ? handler : null);
    1427    }
    1428
    1429    /**
    1430     * A specialized {@code ConversionHandler} implementation which allows overriding the date format pattern. This class
    1431     * takes care that the format pattern can be defined as a property of the wrapped configuration or temporarily passed
    1432     * when calling a conversion method.
    1433     */
    1434    private final class DataConversionHandler extends DefaultConversionHandler {
    1435        /**
    1436         * {@inheritDoc} This implementation checks for a defined data format in the following order:
    1437         * <ul>
    1438         * <li>If a temporary date format is set for the current call, it is used.</li>
    1439         * <li>If a date format is specified in this configuration using the {@code DATE_FORMAT_KEY} property, it is used.</li>
    1440         * <li>Otherwise, the date format set for the original conversion handler is used if available.</li>
    1441         * </ul>
    1442         */
    1443        @Override
    1444        public String getDateFormat() {
    1445            if (StringUtils.isNotEmpty(TEMP_DATE_FORMAT.get())) {
    1446                return TEMP_DATE_FORMAT.get();
    1447            }
    1448            if (containsKey(DATE_FORMAT_KEY)) {
    1449                return getDefaultDateFormat();
    1450            }
    1451
    1452            final DefaultConversionHandler orgHandler = getOriginalConversionHandler();
    1453            return orgHandler != null ? orgHandler.getDateFormat() : null;
    1454        }
    1455    }
    1456}