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