DataConfiguration.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.configuration2;

  18. import java.awt.Color;
  19. import java.math.BigDecimal;
  20. import java.math.BigInteger;
  21. import java.net.URI;
  22. import java.net.URL;
  23. import java.util.ArrayList;
  24. import java.util.Calendar;
  25. import java.util.Date;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.Locale;
  29. import java.util.NoSuchElementException;
  30. import java.util.Objects;
  31. import java.util.function.Supplier;

  32. import org.apache.commons.configuration2.convert.ConversionHandler;
  33. import org.apache.commons.configuration2.convert.DefaultConversionHandler;
  34. import org.apache.commons.configuration2.ex.ConversionException;
  35. import org.apache.commons.lang3.ArrayUtils;
  36. import org.apache.commons.lang3.StringUtils;

  37. /**
  38.  * Decorator providing additional getters for any Configuration. This extended Configuration supports more types:
  39.  * <ul>
  40.  * <li>{@link java.net.URL}</li>
  41.  * <li>{@link java.util.Locale}</li>
  42.  * <li>{@link java.util.Date}</li>
  43.  * <li>{@link java.util.Calendar}</li>
  44.  * <li>{@link java.awt.Color}</li>
  45.  * <li>{@link java.net.InetAddress}</li>
  46.  * <li>{@code javax.mail.internet.InternetAddress} (requires Javamail in the classpath)</li>
  47.  * <li>{@code jakarta.mail.internet.InternetAddress} (requires Javamail 2.+ in the classpath)</li>
  48.  * <li>{@link Enum}</li>
  49.  * </ul>
  50.  *
  51.  * Lists and arrays are available for all types.<br>
  52.  * Note that this class is only a thin wrapper over functionality already provided by {@link AbstractConfiguration}.
  53.  * Basically, the generic {@code get()}, and {@code getCollection()} methods are used to actually perform data
  54.  * conversions.
  55.  *
  56.  * <p>
  57.  * <strong>Example</strong>
  58.  * </p>
  59.  *
  60.  * Configuration file {@code config.properties}:
  61.  *
  62.  * <pre>
  63.  * title.color = #0000FF
  64.  * remote.host = 192.168.0.53
  65.  * default.locales = fr,en,de
  66.  * email.contact = dev@test.org, tester@test.org
  67.  * </pre>
  68.  *
  69.  * Usage:
  70.  *
  71.  * <pre>
  72.  * DataConfiguration config = new DataConfiguration(new PropertiesConfiguration("config.properties"));
  73.  *
  74.  * // retrieve a property using a specialized getter
  75.  * Color color = config.getColor("title.color");
  76.  *
  77.  * // retrieve a property using a generic getter
  78.  * InetAddress host = (InetAddress) config.get(InetAddress.class, "remote.host");
  79.  * Locale[] locales = (Locale[]) config.getArray(Locale.class, "default.locales");
  80.  * List contacts = config.getList(InternetAddress.class, "email.contact");
  81.  * </pre>
  82.  *
  83.  * <p>
  84.  * <strong>Dates</strong>
  85.  * </p>
  86.  *
  87.  * Date objects are expected to be formatted with the pattern {@code yyyy-MM-dd HH:mm:ss}. This default format can be
  88.  * changed by specifying another format in the getters, or by putting a date format in the configuration under the key
  89.  * {@code org.apache.commons.configuration.format.date}. Alternatively, the date format can also be specified via the
  90.  * {@code ConversionHandler} used by a configuration instance:
  91.  *
  92.  * <pre>
  93.  * DefaultConversionHandler handler = new DefaultConversionHandler();
  94.  * handler.setDateFormat("mm/dd/yyyy");
  95.  * config.setConversionHandler(handler);
  96.  * </pre>
  97.  *
  98.  * @since 1.1
  99.  */
  100. public class DataConfiguration extends AbstractConfiguration {

  101.     /**
  102.      * A specialized {@code ConversionHandler} implementation which allows overriding the date format pattern. This class
  103.      * takes care that the format pattern can be defined as a property of the wrapped configuration or temporarily passed
  104.      * when calling a conversion method.
  105.      */
  106.     private final class DataConversionHandler extends DefaultConversionHandler {
  107.         /**
  108.          * {@inheritDoc} This implementation checks for a defined data format in the following order:
  109.          * <ul>
  110.          * <li>If a temporary date format is set for the current call, it is used.</li>
  111.          * <li>If a date format is specified in this configuration using the {@code DATE_FORMAT_KEY} property, it is used.</li>
  112.          * <li>Otherwise, the date format set for the original conversion handler is used if available.</li>
  113.          * </ul>
  114.          */
  115.         @Override
  116.         public String getDateFormat() {
  117.             if (StringUtils.isNotEmpty(TEMP_DATE_FORMAT.get())) {
  118.                 return TEMP_DATE_FORMAT.get();
  119.             }
  120.             if (containsKey(DATE_FORMAT_KEY)) {
  121.                 return getDefaultDateFormat();
  122.             }

  123.             final DefaultConversionHandler orgHandler = getOriginalConversionHandler();
  124.             return orgHandler != null ? orgHandler.getDateFormat() : null;
  125.         }
  126.     }

  127.     /** The key of the property storing the user-defined date format. */
  128.     public static final String DATE_FORMAT_KEY = "org.apache.commons.configuration.format.date";

  129.     /** The default format for dates. */
  130.     public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

  131.     /** Empty array constant. */
  132.     private static final URL[] EMPTY_URL_ARRAY = {};

  133.     /** Empty array constant. */
  134.     private static final URI[] EMPTY_URI_ARRAY = {};

  135.     /** Empty array constant. */
  136.     private static final Locale[] EMPTY_LOCALE_ARRAY = {};

  137.     /** Empty array constant. */
  138.     private static final Date[] EMPTY_DATE_ARRAY = {};

  139.     /** Empty array constant. */
  140.     private static final Color[] EMPTY_COLOR_ARRAY = {};

  141.     /** Empty array constant. */
  142.     private static final Calendar[] EMPTY_CALENDARD_ARRAY = {};

  143.     /** Empty array constant. */
  144.     private static final BigInteger[] EMPTY_BIG_INTEGER_ARRAY = {};

  145.     /** Empty array constant. */
  146.     private static final BigDecimal[] EMPTY_BIG_DECIMAL_ARRAY = {};

  147.     /** Stores temporary date formats. */
  148.     private static final ThreadLocal<String> TEMP_DATE_FORMAT = new ThreadLocal<>();

  149.     /** Stores the wrapped configuration. */
  150.     private final Configuration configuration;

  151.     /** A special conversion handler object used by this configuration. */
  152.     private final ConversionHandler dataConversionHandler;

  153.     /**
  154.      * Creates a new instance of {@code DataConfiguration} and sets the wrapped configuration.
  155.      *
  156.      * @param configuration the wrapped configuration
  157.      */
  158.     public DataConfiguration(final Configuration configuration) {
  159.         this.configuration = Objects.requireNonNull(configuration, "configuration");
  160.         this.dataConversionHandler = new DataConversionHandler();
  161.     }

  162.     @Override
  163.     protected void addPropertyDirect(final String key, final Object value) {
  164.         if (configuration instanceof AbstractConfiguration) {
  165.             ((AbstractConfiguration) configuration).addPropertyDirect(key, value);
  166.         } else {
  167.             configuration.addProperty(key, value);
  168.         }
  169.     }

  170.     @Override
  171.     protected void addPropertyInternal(final String key, final Object obj) {
  172.         configuration.addProperty(key, obj);
  173.     }

  174.     private <R> R applyTempDateFormat(final String format, final Supplier<R> supplier) {
  175.         TEMP_DATE_FORMAT.set(format);
  176.         try {
  177.             return supplier.get();
  178.         } finally {
  179.             TEMP_DATE_FORMAT.remove();
  180.         }
  181.     }

  182.     @Override
  183.     protected void clearPropertyDirect(final String key) {
  184.         configuration.clearProperty(key);
  185.     }

  186.     @Override
  187.     protected boolean containsKeyInternal(final String key) {
  188.         return configuration.containsKey(key);
  189.     }

  190.     /**
  191.      * Tests whether this configuration contains one or more matches to this value. This operation stops at first
  192.      * match but may be more expensive than the containsKey method.
  193.      * @since 2.11.0
  194.      */
  195.     @Override
  196.     protected boolean containsValueInternal(final Object value) {
  197.         return configuration.containsValue(value);
  198.     }

  199.     /**
  200.      * Gets an array of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
  201.      * an empty array is returned.
  202.      *
  203.      * @param key The configuration key.
  204.      * @return The associated BigDecimal array if the key is found.
  205.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
  206.      */
  207.     public BigDecimal[] getBigDecimalArray(final String key) {
  208.         return getBigDecimalArray(key, EMPTY_BIG_DECIMAL_ARRAY);
  209.     }

  210.     /**
  211.      * Gets an array of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
  212.      * an empty array is returned.
  213.      *
  214.      * @param key The configuration key.
  215.      * @param defaultValue the default value, which will be returned if the property is not found
  216.      * @return The associated BigDecimal array if the key is found.
  217.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
  218.      */
  219.     public BigDecimal[] getBigDecimalArray(final String key, final BigDecimal... defaultValue) {
  220.         return get(BigDecimal[].class, key, defaultValue);
  221.     }

  222.     /**
  223.      * Gets a list of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
  224.      * an empty list is returned.
  225.      *
  226.      * @param key The configuration key.
  227.      * @return The associated BigDecimal list if the key is found.
  228.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
  229.      */
  230.     public List<BigDecimal> getBigDecimalList(final String key) {
  231.         return getBigDecimalList(key, new ArrayList<>());
  232.     }

  233.     /**
  234.      * Gets a list of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object,
  235.      * the default value is returned.
  236.      *
  237.      * @param key The configuration key.
  238.      * @param defaultValue The default value.
  239.      * @return The associated List of BigDecimals.
  240.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
  241.      */
  242.     public List<BigDecimal> getBigDecimalList(final String key, final List<BigDecimal> defaultValue) {
  243.         return getList(BigDecimal.class, key, defaultValue);
  244.     }

  245.     /**
  246.      * Gets an array of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
  247.      * an empty array is returned.
  248.      *
  249.      * @param key The configuration key.
  250.      * @return The associated BigInteger array if the key is found.
  251.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
  252.      */
  253.     public BigInteger[] getBigIntegerArray(final String key) {
  254.         return getBigIntegerArray(key, EMPTY_BIG_INTEGER_ARRAY);
  255.     }

  256.     /**
  257.      * Gets an array of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
  258.      * an empty array is returned.
  259.      *
  260.      * @param key The configuration key.
  261.      * @param defaultValue the default value, which will be returned if the property is not found
  262.      * @return The associated BigInteger array if the key is found.
  263.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
  264.      */
  265.     public BigInteger[] getBigIntegerArray(final String key, final BigInteger... defaultValue) {
  266.         return get(BigInteger[].class, key, defaultValue);
  267.     }

  268.     /**
  269.      * Gets a list of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
  270.      * an empty list is returned.
  271.      *
  272.      * @param key The configuration key.
  273.      * @return The associated BigInteger list if the key is found.
  274.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
  275.      */
  276.     public List<BigInteger> getBigIntegerList(final String key) {
  277.         return getBigIntegerList(key, new ArrayList<>());
  278.     }

  279.     /**
  280.      * Gets a list of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object,
  281.      * the default value is returned.
  282.      *
  283.      * @param key The configuration key.
  284.      * @param defaultValue The default value.
  285.      * @return The associated List of BigIntegers.
  286.      * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
  287.      */
  288.     public List<BigInteger> getBigIntegerList(final String key, final List<BigInteger> defaultValue) {
  289.         return getList(BigInteger.class, key, defaultValue);
  290.     }

  291.     /**
  292.      * Gets an array of boolean primitives associated with the given configuration key. If the key doesn't map to an existing
  293.      * object an empty array is returned.
  294.      *
  295.      * @param key The configuration key.
  296.      * @return The associated boolean array if the key is found.
  297.      * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
  298.      */
  299.     public boolean[] getBooleanArray(final String key) {
  300.         return (boolean[]) getArray(Boolean.TYPE, key);
  301.     }

  302.     /**
  303.      * Gets an array of boolean primitives associated with the given configuration key. If the key doesn't map to an existing
  304.      * object, the default value is returned.
  305.      *
  306.      * @param key The configuration key.
  307.      * @param defaultValue The default value.
  308.      * @return The associated boolean array if the key is found.
  309.      * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
  310.      */
  311.     public boolean[] getBooleanArray(final String key, final boolean... defaultValue) {
  312.         return get(boolean[].class, key, defaultValue);
  313.     }

  314.     /**
  315.      * Gets a list of Boolean objects associated with the given configuration key. If the key doesn't map to an existing
  316.      * object an empty list is returned.
  317.      *
  318.      * @param key The configuration key.
  319.      * @return The associated Boolean list if the key is found.
  320.      * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
  321.      */
  322.     public List<Boolean> getBooleanList(final String key) {
  323.         return getBooleanList(key, new ArrayList<>());
  324.     }

  325.     /**
  326.      * Gets a list of Boolean objects associated with the given configuration key. If the key doesn't map to an existing
  327.      * object, the default value is returned.
  328.      *
  329.      * @param key The configuration key.
  330.      * @param defaultValue The default value.
  331.      * @return The associated List of Booleans.
  332.      * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
  333.      */
  334.     public List<Boolean> getBooleanList(final String key, final List<Boolean> defaultValue) {
  335.         return getList(Boolean.class, key, defaultValue);
  336.     }

  337.     /**
  338.      * Gets an array of byte primitives associated with the given configuration key. If the key doesn't map to an existing
  339.      * object an empty array is returned.
  340.      *
  341.      * @param key The configuration key.
  342.      * @return The associated byte array if the key is found.
  343.      * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
  344.      */
  345.     public byte[] getByteArray(final String key) {
  346.         return getByteArray(key, ArrayUtils.EMPTY_BYTE_ARRAY);
  347.     }

  348.     /**
  349.      * Gets an array of byte 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.      * @param defaultValue the default value, which will be returned if the property is not found
  354.      * @return The associated byte array if the key is found.
  355.      * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
  356.      */
  357.     public byte[] getByteArray(final String key, final byte... defaultValue) {
  358.         return get(byte[].class, key, defaultValue);
  359.     }

  360.     /**
  361.      * Gets a list of Byte objects associated with the given configuration key. If the key doesn't map to an existing object
  362.      * an empty list is returned.
  363.      *
  364.      * @param key The configuration key.
  365.      * @return The associated Byte list if the key is found.
  366.      * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
  367.      */
  368.     public List<Byte> getByteList(final String key) {
  369.         return getByteList(key, new ArrayList<>());
  370.     }

  371.     /**
  372.      * Gets a list of Byte objects associated with the given configuration key. If the key doesn't map to an existing object,
  373.      * the default value is returned.
  374.      *
  375.      * @param key The configuration key.
  376.      * @param defaultValue The default value.
  377.      * @return The associated List of Bytes.
  378.      * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
  379.      */
  380.     public List<Byte> getByteList(final String key, final List<Byte> defaultValue) {
  381.         return getList(Byte.class, key, defaultValue);
  382.     }

  383.     /**
  384.      * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
  385.      * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  386.      * {@link #DEFAULT_DATE_FORMAT} pattern.
  387.      *
  388.      * @param key The configuration key.
  389.      * @return The associated Calendar.
  390.      * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
  391.      */
  392.     public Calendar getCalendar(final String key) {
  393.         return get(Calendar.class, key);
  394.     }

  395.     /**
  396.      * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
  397.      * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  398.      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
  399.      *
  400.      * @param key The configuration key.
  401.      * @param defaultValue The default value.
  402.      * @return The associated Calendar.
  403.      * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
  404.      */
  405.     public Calendar getCalendar(final String key, final Calendar defaultValue) {
  406.         return getCalendar(key, defaultValue, null);
  407.     }

  408.     /**
  409.      * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
  410.      * specified format pattern. If the key doesn't map to an existing object, the default value is returned.
  411.      *
  412.      * @param key The configuration key.
  413.      * @param defaultValue The default value.
  414.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  415.      * @return The associated Calendar.
  416.      * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
  417.      */
  418.     public Calendar getCalendar(final String key, final Calendar defaultValue, final String format) {
  419.         return applyTempDateFormat(format, () -> get(Calendar.class, key, defaultValue));
  420.     }

  421.     /**
  422.      * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
  423.      * specified format pattern.
  424.      *
  425.      * @param key The configuration key.
  426.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  427.      * @return The associated Calendar
  428.      * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
  429.      */
  430.     public Calendar getCalendar(final String key, final String format) {
  431.         final Calendar value = getCalendar(key, null, format);
  432.         if (value != null) {
  433.             return value;
  434.         }
  435.         if (isThrowExceptionOnMissing()) {
  436.             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
  437.         }
  438.         return null;
  439.     }

  440.     /**
  441.      * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
  442.      * will be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
  443.      * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is
  444.      * returned.
  445.      *
  446.      * @param key The configuration key.
  447.      * @return The associated Calendar array if the key is found.
  448.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  449.      */
  450.     public Calendar[] getCalendarArray(final String key) {
  451.         return getCalendarArray(key, EMPTY_CALENDARD_ARRAY);
  452.     }

  453.     /**
  454.      * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
  455.      * will be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
  456.      * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is
  457.      * returned.
  458.      *
  459.      * @param key The configuration key.
  460.      * @param defaultValue the default value, which will be returned if the property is not found
  461.      * @return The associated Calendar array if the key is found.
  462.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  463.      */
  464.     public Calendar[] getCalendarArray(final String key, final Calendar... defaultValue) {
  465.         return getCalendarArray(key, defaultValue, null);
  466.     }

  467.     /**
  468.      * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
  469.      * will be parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
  470.      * returned.
  471.      *
  472.      * @param key The configuration key.
  473.      * @param defaultValue The default value.
  474.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  475.      * @return The associated Calendar array if the key is found.
  476.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  477.      */
  478.     public Calendar[] getCalendarArray(final String key, final Calendar[] defaultValue, final String format) {
  479.         return applyTempDateFormat(format, () -> get(Calendar[].class, key, defaultValue));
  480.     }

  481.     /**
  482.      * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
  483.      * will be parsed with the specified format pattern. If the key doesn't map to an existing object an empty array is
  484.      * returned.
  485.      *
  486.      * @param key The configuration key.
  487.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  488.      * @return The associated Calendar array if the key is found.
  489.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  490.      */
  491.     public Calendar[] getCalendarArray(final String key, final String format) {
  492.         return getCalendarArray(key, EMPTY_CALENDARD_ARRAY, format);
  493.     }

  494.     /**
  495.      * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
  496.      * be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with
  497.      * the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty list is returned.
  498.      *
  499.      * @param key The configuration key.
  500.      * @return The associated Calendar list if the key is found.
  501.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  502.      */
  503.     public List<Calendar> getCalendarList(final String key) {
  504.         return getCalendarList(key, new ArrayList<>());
  505.     }

  506.     /**
  507.      * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
  508.      * be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with
  509.      * the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is
  510.      * returned.
  511.      *
  512.      * @param key The configuration key.
  513.      * @param defaultValue The default value.
  514.      * @return The associated Calendar list if the key is found.
  515.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  516.      */
  517.     public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue) {
  518.         return getCalendarList(key, defaultValue, null);
  519.     }

  520.     /**
  521.      * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
  522.      * be parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
  523.      * returned.
  524.      *
  525.      * @param key The configuration key.
  526.      * @param defaultValue The default value.
  527.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  528.      * @return The associated Calendar list if the key is found.
  529.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  530.      */
  531.     public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue, final String format) {
  532.         return applyTempDateFormat(format, () -> getList(Calendar.class, key, defaultValue));
  533.     }

  534.     /**
  535.      * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
  536.      * be parsed with the specified format pattern. If the key doesn't map to an existing object an empty list is returned.
  537.      *
  538.      * @param key The configuration key.
  539.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  540.      * @return The associated Calendar list if the key is found.
  541.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
  542.      */
  543.     public List<Calendar> getCalendarList(final String key, final String format) {
  544.         return getCalendarList(key, new ArrayList<>(), format);
  545.     }

  546.     /**
  547.      * Gets a Color associated with the given configuration key.
  548.      *
  549.      * @param key The configuration key.
  550.      * @return The associated Color.
  551.      * @throws ConversionException is thrown if the key maps to an object that is not a Color.
  552.      */
  553.     public Color getColor(final String key) {
  554.         return get(Color.class, key);
  555.     }

  556.     /**
  557.      * Gets a Color associated with the given configuration key. If the key doesn't map to an existing object, the default
  558.      * value is returned.
  559.      *
  560.      * @param key The configuration key.
  561.      * @param defaultValue The default value.
  562.      * @return The associated Color.
  563.      * @throws ConversionException is thrown if the key maps to an object that is not a Color.
  564.      */
  565.     public Color getColor(final String key, final Color defaultValue) {
  566.         return get(Color.class, key, defaultValue);
  567.     }

  568.     /**
  569.      * Gets an array of Colors associated with the given configuration key. If the key doesn't map to an existing object an
  570.      * empty array is returned.
  571.      *
  572.      * @param key The configuration key.
  573.      * @return The associated Color array if the key is found.
  574.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
  575.      */
  576.     public Color[] getColorArray(final String key) {
  577.         return getColorArray(key, EMPTY_COLOR_ARRAY);
  578.     }

  579.     /**
  580.      * Gets an array of Colors associated with the given configuration key. If the key doesn't map to an existing object an
  581.      * empty array is returned.
  582.      *
  583.      * @param key The configuration key.
  584.      * @param defaultValue the default value, which will be returned if the property is not found
  585.      * @return The associated Color array if the key is found.
  586.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
  587.      */
  588.     public Color[] getColorArray(final String key, final Color... defaultValue) {
  589.         return get(Color[].class, key, defaultValue);
  590.     }

  591.     /**
  592.      * Gets a list of Colors associated with the given configuration key. If the key doesn't map to an existing object an
  593.      * empty list is returned.
  594.      *
  595.      * @param key The configuration key.
  596.      * @return The associated Color list if the key is found.
  597.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
  598.      */
  599.     public List<Color> getColorList(final String key) {
  600.         return getColorList(key, new ArrayList<>());
  601.     }

  602.     /**
  603.      * Gets a list of Colors associated with the given configuration key. If the key doesn't map to an existing object, the
  604.      * default value is returned.
  605.      *
  606.      * @param key The configuration key.
  607.      * @param defaultValue The default value.
  608.      * @return The associated List of Colors.
  609.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
  610.      */
  611.     public List<Color> getColorList(final String key, final List<Color> defaultValue) {
  612.         return getList(Color.class, key, defaultValue);
  613.     }

  614.     /**
  615.      * Gets the configuration decorated by this DataConfiguration.
  616.      *
  617.      * @return the wrapped configuration
  618.      */
  619.     public Configuration getConfiguration() {
  620.         return configuration;
  621.     }

  622.     /**
  623.      * {@inheritDoc} This implementation returns the special conversion handler used by this configuration instance.
  624.      */
  625.     @Override
  626.     public ConversionHandler getConversionHandler() {
  627.         return dataConversionHandler;
  628.     }

  629.     /**
  630.      * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
  631.      * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  632.      * {@link #DEFAULT_DATE_FORMAT} pattern.
  633.      *
  634.      * @param key The configuration key.
  635.      * @return The associated Date.
  636.      * @throws ConversionException is thrown if the key maps to an object that is not a Date.
  637.      */
  638.     public Date getDate(final String key) {
  639.         return get(Date.class, key);
  640.     }

  641.     /**
  642.      * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
  643.      * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  644.      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
  645.      *
  646.      * @param key The configuration key.
  647.      * @param defaultValue The default value.
  648.      * @return The associated Date.
  649.      * @throws ConversionException is thrown if the key maps to an object that is not a Date.
  650.      */
  651.     public Date getDate(final String key, final Date defaultValue) {
  652.         return getDate(key, defaultValue, null);
  653.     }

  654.     /**
  655.      * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
  656.      * specified format pattern. If the key doesn't map to an existing object, the default value is returned.
  657.      *
  658.      * @param key The configuration key.
  659.      * @param defaultValue The default value.
  660.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  661.      * @return The associated Date.
  662.      * @throws ConversionException is thrown if the key maps to an object that is not a Date.
  663.      */
  664.     public Date getDate(final String key, final Date defaultValue, final String format) {
  665.         return applyTempDateFormat(format, () -> get(Date.class, key, defaultValue));
  666.     }

  667.     /**
  668.      * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
  669.      * specified format pattern.
  670.      *
  671.      * @param key The configuration key.
  672.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  673.      * @return The associated Date
  674.      * @throws ConversionException is thrown if the key maps to an object that is not a Date.
  675.      */
  676.     public Date getDate(final String key, final String format) {
  677.         final Date value = getDate(key, null, format);
  678.         if (value != null) {
  679.             return value;
  680.         }
  681.         if (isThrowExceptionOnMissing()) {
  682.             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
  683.         }
  684.         return null;
  685.     }

  686.     /**
  687.      * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  688.      * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  689.      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is returned.
  690.      *
  691.      * @param key The configuration key.
  692.      * @return The associated Date array if the key is found.
  693.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  694.      */
  695.     public Date[] getDateArray(final String key) {
  696.         return getDateArray(key, EMPTY_DATE_ARRAY);
  697.     }

  698.     /**
  699.      * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  700.      * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  701.      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is returned.
  702.      *
  703.      * @param key The configuration key.
  704.      * @param defaultValue the default value, which will be returned if the property is not found
  705.      * @return The associated Date array if the key is found.
  706.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  707.      */
  708.     public Date[] getDateArray(final String key, final Date... defaultValue) {
  709.         return getDateArray(key, defaultValue, null);
  710.     }

  711.     /**
  712.      * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  713.      * parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
  714.      * returned.
  715.      *
  716.      * @param key The configuration key.
  717.      * @param defaultValue The default value.
  718.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  719.      * @return The associated Date array if the key is found.
  720.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  721.      */
  722.     public Date[] getDateArray(final String key, final Date[] defaultValue, final String format) {
  723.         return applyTempDateFormat(format, () -> get(Date[].class, key, defaultValue));
  724.     }

  725.     /**
  726.      * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  727.      * parsed with the specified format pattern. If the key doesn't map to an existing object an empty array is returned.
  728.      *
  729.      * @param key The configuration key.
  730.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  731.      * @return The associated Date array if the key is found.
  732.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  733.      */
  734.     public Date[] getDateArray(final String key, final String format) {
  735.         return getDateArray(key, EMPTY_DATE_ARRAY, format);
  736.     }

  737.     /**
  738.      * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  739.      * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  740.      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, a new list is returned.
  741.      *
  742.      * @param key The configuration key.
  743.      * @return The associated Date list if the key is found. If the key doesn't map to an existing object, a new list is returned.
  744.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  745.      */
  746.     public List<Date> getDateList(final String key) {
  747.         return getDateList(key, new ArrayList<>());
  748.     }

  749.     /**
  750.      * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  751.      * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
  752.      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
  753.      *
  754.      * @param key The configuration key.
  755.      * @param defaultValue The default value.
  756.      * @return The associated Date list if the key is found.
  757.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  758.      */
  759.     public List<Date> getDateList(final String key, final List<Date> defaultValue) {
  760.         return getDateList(key, defaultValue, null);
  761.     }

  762.     /**
  763.      * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  764.      * parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
  765.      * returned.
  766.      *
  767.      * @param key The configuration key.
  768.      * @param defaultValue The default value.
  769.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  770.      * @return The associated Date list if the key is found.
  771.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  772.      */
  773.     public List<Date> getDateList(final String key, final List<Date> defaultValue, final String format) {
  774.         return applyTempDateFormat(format, () -> getList(Date.class, key, defaultValue));
  775.     }

  776.     /**
  777.      * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
  778.      * parsed with the specified format pattern. If the key doesn't map to an existing object an empty list is returned.
  779.      *
  780.      * @param key The configuration key.
  781.      * @param format The non-localized {@link java.text.DateFormat} pattern.
  782.      * @return The associated Date list if the key is found.
  783.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
  784.      */
  785.     public List<Date> getDateList(final String key, final String format) {
  786.         return getDateList(key, new ArrayList<>(), format);
  787.     }

  788.     /**
  789.      * Gets the date format specified by the user in the DATE_FORMAT_KEY property, or the default format otherwise.
  790.      *
  791.      * @return the default date format
  792.      */
  793.     private String getDefaultDateFormat() {
  794.         return getString(DATE_FORMAT_KEY, DEFAULT_DATE_FORMAT);
  795.     }

  796.     /**
  797.      * Gets an array of double primitives associated with the given configuration key. If the key doesn't map to an existing
  798.      * object an empty array is returned.
  799.      *
  800.      * @param key The configuration key.
  801.      * @return The associated double array if the key is found.
  802.      * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
  803.      */
  804.     public double[] getDoubleArray(final String key) {
  805.         return getDoubleArray(key, ArrayUtils.EMPTY_DOUBLE_ARRAY);
  806.     }

  807.     /**
  808.      * Gets an array of double primitives associated with the given configuration key. If the key doesn't map to an existing
  809.      * object an empty array is returned.
  810.      *
  811.      * @param key The configuration key.
  812.      * @param defaultValue the default value, which will be returned if the property is not found
  813.      * @return The associated double array if the key is found.
  814.      * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
  815.      */
  816.     public double[] getDoubleArray(final String key, final double... defaultValue) {
  817.         return get(double[].class, key, defaultValue);
  818.     }

  819.     /**
  820.      * Gets a list of Double objects associated with the given configuration key. If the key doesn't map to an existing
  821.      * object an empty list is returned.
  822.      *
  823.      * @param key The configuration key.
  824.      * @return The associated Double list if the key is found.
  825.      * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
  826.      */
  827.     public List<Double> getDoubleList(final String key) {
  828.         return getDoubleList(key, new ArrayList<>());
  829.     }

  830.     /**
  831.      * Gets a list of Double objects associated with the given configuration key. If the key doesn't map to an existing
  832.      * object, the default value is returned.
  833.      *
  834.      * @param key The configuration key.
  835.      * @param defaultValue The default value.
  836.      * @return The associated List of Doubles.
  837.      * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
  838.      */
  839.     public List<Double> getDoubleList(final String key, final List<Double> defaultValue) {
  840.         return getList(Double.class, key, defaultValue);
  841.     }

  842.     /**
  843.      * Gets an array of float primitives associated with the given configuration key. If the key doesn't map to an existing
  844.      * object an empty array is returned.
  845.      *
  846.      * @param key The configuration key.
  847.      * @return The associated float array if the key is found.
  848.      * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
  849.      */
  850.     public float[] getFloatArray(final String key) {
  851.         return getFloatArray(key, ArrayUtils.EMPTY_FLOAT_ARRAY);
  852.     }

  853.     /**
  854.      * Gets an array of float primitives associated with the given configuration key. If the key doesn't map to an existing
  855.      * object an empty array is returned.
  856.      *
  857.      * @param key The configuration key.
  858.      * @param defaultValue the default value, which will be returned if the property is not found
  859.      * @return The associated float array if the key is found.
  860.      * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
  861.      */
  862.     public float[] getFloatArray(final String key, final float... defaultValue) {
  863.         return get(float[].class, key, defaultValue);
  864.     }

  865.     /**
  866.      * Gets a list of Float objects associated with the given configuration key. If the key doesn't map to an existing object
  867.      * an empty list is returned.
  868.      *
  869.      * @param key The configuration key.
  870.      * @return The associated Float list if the key is found.
  871.      * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
  872.      */
  873.     public List<Float> getFloatList(final String key) {
  874.         return getFloatList(key, new ArrayList<>());
  875.     }

  876.     /**
  877.      * Gets a list of Float objects associated with the given configuration key. If the key doesn't map to an existing
  878.      * object, the default value is returned.
  879.      *
  880.      * @param key The configuration key.
  881.      * @param defaultValue The default value.
  882.      * @return The associated List of Floats.
  883.      * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
  884.      */
  885.     public List<Float> getFloatList(final String key, final List<Float> defaultValue) {
  886.         return getList(Float.class, key, defaultValue);
  887.     }

  888.     /**
  889.      * Gets an array of int primitives associated with the given configuration key. If the key doesn't map to an existing
  890.      * object an empty array is returned.
  891.      *
  892.      * @param key The configuration key.
  893.      * @return The associated int array if the key is found.
  894.      * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
  895.      */
  896.     public int[] getIntArray(final String key) {
  897.         return getIntArray(key, ArrayUtils.EMPTY_INT_ARRAY);
  898.     }

  899.     /**
  900.      * Gets an array of int primitives associated with the given configuration key. If the key doesn't map to an existing
  901.      * object an empty array is returned.
  902.      *
  903.      * @param key The configuration key.
  904.      * @param defaultValue the default value, which will be returned if the property is not found
  905.      * @return The associated int array if the key is found.
  906.      * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
  907.      */
  908.     public int[] getIntArray(final String key, final int... defaultValue) {
  909.         return get(int[].class, key, defaultValue);
  910.     }

  911.     /**
  912.      * Gets a list of Integer objects associated with the given configuration key. If the key doesn't map to an existing
  913.      * object an empty list is returned.
  914.      *
  915.      * @param key The configuration key.
  916.      * @return The associated Integer list if the key is found.
  917.      * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
  918.      */
  919.     public List<Integer> getIntegerList(final String key) {
  920.         return getIntegerList(key, new ArrayList<>());
  921.     }

  922.     /**
  923.      * Gets a list of Integer objects associated with the given configuration key. If the key doesn't map to an existing
  924.      * object, the default value is returned.
  925.      *
  926.      * @param key The configuration key.
  927.      * @param defaultValue The default value.
  928.      * @return The associated List of Integers.
  929.      * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
  930.      */
  931.     public List<Integer> getIntegerList(final String key, final List<Integer> defaultValue) {
  932.         return getList(Integer.class, key, defaultValue);
  933.     }

  934.     @Override
  935.     protected Iterator<String> getKeysInternal() {
  936.         return configuration.getKeys();
  937.     }

  938.     /**
  939.      * Gets a Locale associated with the given configuration key.
  940.      *
  941.      * @param key The configuration key.
  942.      * @return The associated Locale.
  943.      * @throws ConversionException is thrown if the key maps to an object that is not a Locale.
  944.      */
  945.     public Locale getLocale(final String key) {
  946.         return get(Locale.class, key);
  947.     }

  948.     /**
  949.      * Gets a Locale associated with the given configuration key. If the key doesn't map to an existing object, the default
  950.      * value is returned.
  951.      *
  952.      * @param key The configuration key.
  953.      * @param defaultValue The default value.
  954.      * @return The associated Locale.
  955.      * @throws ConversionException is thrown if the key maps to an object that is not a Locale.
  956.      */
  957.     public Locale getLocale(final String key, final Locale defaultValue) {
  958.         return get(Locale.class, key, defaultValue);
  959.     }

  960.     /**
  961.      * Gets an array of Locales associated with the given configuration key. If the key doesn't map to an existing object an
  962.      * empty array is returned.
  963.      *
  964.      * @param key The configuration key.
  965.      * @return The associated Locale array if the key is found.
  966.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
  967.      */
  968.     public Locale[] getLocaleArray(final String key) {
  969.         return getLocaleArray(key, EMPTY_LOCALE_ARRAY);
  970.     }

  971.     /**
  972.      * Gets an array of Locales associated with the given configuration key. If the key doesn't map to an existing object an
  973.      * empty array is returned.
  974.      *
  975.      * @param key The configuration key.
  976.      * @param defaultValue the default value, which will be returned if the property is not found
  977.      * @return The associated Locale array if the key is found.
  978.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
  979.      */
  980.     public Locale[] getLocaleArray(final String key, final Locale... defaultValue) {
  981.         return get(Locale[].class, key, defaultValue);
  982.     }

  983.     /**
  984.      * Gets a list of Locales associated with the given configuration key. If the key doesn't map to an existing object an
  985.      * empty list is returned.
  986.      *
  987.      * @param key The configuration key.
  988.      * @return The associated Locale list if the key is found.
  989.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
  990.      */
  991.     public List<Locale> getLocaleList(final String key) {
  992.         return getLocaleList(key, new ArrayList<>());
  993.     }

  994.     /**
  995.      * Gets a list of Locales associated with the given configuration key. If the key doesn't map to an existing object, the
  996.      * default value is returned.
  997.      *
  998.      * @param key The configuration key.
  999.      * @param defaultValue The default value.
  1000.      * @return The associated List of Locales.
  1001.      * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
  1002.      */
  1003.     public List<Locale> getLocaleList(final String key, final List<Locale> defaultValue) {
  1004.         return getList(Locale.class, key, defaultValue);
  1005.     }

  1006.     /**
  1007.      * Gets an array of long primitives associated with the given configuration key. If the key doesn't map to an existing
  1008.      * object an empty array is returned.
  1009.      *
  1010.      * @param key The configuration key.
  1011.      * @return The associated long array if the key is found.
  1012.      * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
  1013.      */
  1014.     public long[] getLongArray(final String key) {
  1015.         return getLongArray(key, ArrayUtils.EMPTY_LONG_ARRAY);
  1016.     }

  1017.     /**
  1018.      * Gets an array of long primitives associated with the given configuration key. If the key doesn't map to an existing
  1019.      * object an empty array is returned.
  1020.      *
  1021.      * @param key The configuration key.
  1022.      * @param defaultValue the default value, which will be returned if the property is not found
  1023.      * @return The associated long array if the key is found.
  1024.      * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
  1025.      */
  1026.     public long[] getLongArray(final String key, final long... defaultValue) {
  1027.         return get(long[].class, key, defaultValue);
  1028.     }

  1029.     /**
  1030.      * Gets a list of Long objects associated with the given configuration key. If the key doesn't map to an existing object
  1031.      * an empty list is returned.
  1032.      *
  1033.      * @param key The configuration key.
  1034.      * @return The associated Long list if the key is found.
  1035.      * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
  1036.      */
  1037.     public List<Long> getLongList(final String key) {
  1038.         return getLongList(key, new ArrayList<>());
  1039.     }

  1040.     /**
  1041.      * Gets a list of Long objects associated with the given configuration key. If the key doesn't map to an existing object,
  1042.      * the default value is returned.
  1043.      *
  1044.      * @param key The configuration key.
  1045.      * @param defaultValue The default value.
  1046.      * @return The associated List of Longs.
  1047.      * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
  1048.      */
  1049.     public List<Long> getLongList(final String key, final List<Long> defaultValue) {
  1050.         return getList(Long.class, key, defaultValue);
  1051.     }

  1052.     /**
  1053.      * Gets the original conversion handler set for this configuration. If this is not a
  1054.      * {@code DefaultConversionHandler}, result is <strong>null</strong>.
  1055.      *
  1056.      * @return the original conversion handler or <strong>null</strong>
  1057.      */
  1058.     private DefaultConversionHandler getOriginalConversionHandler() {
  1059.         final ConversionHandler handler = super.getConversionHandler();
  1060.         return (DefaultConversionHandler) (handler instanceof DefaultConversionHandler ? handler : null);
  1061.     }

  1062.     @Override
  1063.     protected Object getPropertyInternal(final String key) {
  1064.         return configuration.getProperty(key);
  1065.     }

  1066.     /**
  1067.      * Gets an array of short primitives associated with the given configuration key. If the key doesn't map to an existing
  1068.      * object an empty array is returned.
  1069.      *
  1070.      * @param key The configuration key.
  1071.      * @return The associated short array if the key is found.
  1072.      * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
  1073.      */
  1074.     public short[] getShortArray(final String key) {
  1075.         return getShortArray(key, ArrayUtils.EMPTY_SHORT_ARRAY);
  1076.     }

  1077.     /**
  1078.      * Gets an array of short primitives associated with the given configuration key. If the key doesn't map to an existing
  1079.      * object an empty array is returned.
  1080.      *
  1081.      * @param key The configuration key.
  1082.      * @param defaultValue the default value, which will be returned if the property is not found
  1083.      * @return The associated short array if the key is found.
  1084.      * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
  1085.      */
  1086.     public short[] getShortArray(final String key, final short... defaultValue) {
  1087.         return get(short[].class, key, defaultValue);
  1088.     }

  1089.     /**
  1090.      * Gets a list of Short objects associated with the given configuration key. If the key doesn't map to an existing object
  1091.      * an empty list is returned.
  1092.      *
  1093.      * @param key The configuration key.
  1094.      * @return The associated Short list if the key is found.
  1095.      * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
  1096.      */
  1097.     public List<Short> getShortList(final String key) {
  1098.         return getShortList(key, new ArrayList<>());
  1099.     }

  1100.     /**
  1101.      * Gets a list of Short objects associated with the given configuration key. If the key doesn't map to an existing
  1102.      * object, the default value is returned.
  1103.      *
  1104.      * @param key The configuration key.
  1105.      * @param defaultValue The default value.
  1106.      * @return The associated List of Shorts.
  1107.      * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
  1108.      */
  1109.     public List<Short> getShortList(final String key, final List<Short> defaultValue) {
  1110.         return getList(Short.class, key, defaultValue);
  1111.     }

  1112.     /**
  1113.      * Gets an URI associated with the given configuration key.
  1114.      *
  1115.      * @param key The configuration key.
  1116.      * @return The associated URI.
  1117.      * @throws ConversionException is thrown if the key maps to an object that is not an URI.
  1118.      */
  1119.     public URI getURI(final String key) {
  1120.         return get(URI.class, key);
  1121.     }

  1122.     /**
  1123.      * Gets an URI associated with the given configuration key. If the key doesn't map to an existing object, the default
  1124.      * value is returned.
  1125.      *
  1126.      * @param key The configuration key.
  1127.      * @param defaultValue The default value.
  1128.      * @return The associated URI.
  1129.      * @throws ConversionException is thrown if the key maps to an object that is not an URI.
  1130.      */
  1131.     public URI getURI(final String key, final URI defaultValue) {
  1132.         return get(URI.class, key, defaultValue);
  1133.     }

  1134.     /**
  1135.      * Gets an array of URIs associated with the given configuration key. If the key doesn't map to an existing object an
  1136.      * empty array is returned.
  1137.      *
  1138.      * @param key The configuration key.
  1139.      * @return The associated URI array if the key is found.
  1140.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
  1141.      */
  1142.     public URI[] getURIArray(final String key) {
  1143.         return getURIArray(key, EMPTY_URI_ARRAY);
  1144.     }

  1145.     /**
  1146.      * Gets an array of URIs associated with the given configuration key. If the key doesn't map to an existing object an
  1147.      * empty array is returned.
  1148.      *
  1149.      * @param key The configuration key.
  1150.      * @param defaultValue the default value, which will be returned if the property is not found
  1151.      * @return The associated URI array if the key is found.
  1152.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
  1153.      */
  1154.     public URI[] getURIArray(final String key, final URI... defaultValue) {
  1155.         return get(URI[].class, key, defaultValue);
  1156.     }

  1157.     /**
  1158.      * Gets a list of URIs associated with the given configuration key. If the key doesn't map to an existing object an empty
  1159.      * list is returned.
  1160.      *
  1161.      * @param key The configuration key.
  1162.      * @return The associated URI list if the key is found.
  1163.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
  1164.      */
  1165.     public List<URI> getURIList(final String key) {
  1166.         return getURIList(key, new ArrayList<>());
  1167.     }

  1168.     /**
  1169.      * Gets a list of URIs associated with the given configuration key. If the key doesn't map to an existing object, the
  1170.      * default value is returned.
  1171.      *
  1172.      * @param key The configuration key.
  1173.      * @param defaultValue The default value.
  1174.      * @return The associated List of URIs.
  1175.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
  1176.      */
  1177.     public List<URI> getURIList(final String key, final List<URI> defaultValue) {
  1178.         return getList(URI.class, key, defaultValue);
  1179.     }

  1180.     /**
  1181.      * Gets an URL associated with the given configuration key.
  1182.      *
  1183.      * @param key The configuration key.
  1184.      * @return The associated URL.
  1185.      * @throws ConversionException is thrown if the key maps to an object that is not an URL.
  1186.      */
  1187.     public URL getURL(final String key) {
  1188.         return get(URL.class, key);
  1189.     }

  1190.     /**
  1191.      * Gets an URL associated with the given configuration key. If the key doesn't map to an existing object, the default
  1192.      * value is returned.
  1193.      *
  1194.      * @param key The configuration key.
  1195.      * @param defaultValue The default value.
  1196.      * @return The associated URL.
  1197.      * @throws ConversionException is thrown if the key maps to an object that is not an URL.
  1198.      */
  1199.     public URL getURL(final String key, final URL defaultValue) {
  1200.         return get(URL.class, key, defaultValue);
  1201.     }

  1202.     /**
  1203.      * Gets an array of URLs associated with the given configuration key. If the key doesn't map to an existing object an
  1204.      * empty array is returned.
  1205.      *
  1206.      * @param key The configuration key.
  1207.      * @return The associated URL array if the key is found.
  1208.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
  1209.      */
  1210.     public URL[] getURLArray(final String key) {
  1211.         return getURLArray(key, EMPTY_URL_ARRAY);
  1212.     }

  1213.     /**
  1214.      * Gets an array of URLs associated with the given configuration key. If the key doesn't map to an existing object an
  1215.      * empty array is returned.
  1216.      *
  1217.      * @param key The configuration key.
  1218.      * @param defaultValue the default value, which will be returned if the property is not found
  1219.      * @return The associated URL array if the key is found.
  1220.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
  1221.      */
  1222.     public URL[] getURLArray(final String key, final URL... defaultValue) {
  1223.         return get(URL[].class, key, defaultValue);
  1224.     }

  1225.     /**
  1226.      * Gets a list of URLs associated with the given configuration key. If the key doesn't map to an existing object an empty
  1227.      * list is returned.
  1228.      *
  1229.      * @param key The configuration key.
  1230.      * @return The associated URL list if the key is found.
  1231.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
  1232.      */
  1233.     public List<URL> getURLList(final String key) {
  1234.         return getURLList(key, new ArrayList<>());
  1235.     }

  1236.     /**
  1237.      * Gets a list of URLs associated with the given configuration key. If the key doesn't map to an existing object, the
  1238.      * default value is returned.
  1239.      *
  1240.      * @param key The configuration key.
  1241.      * @param defaultValue The default value.
  1242.      * @return The associated List of URLs.
  1243.      * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
  1244.      */
  1245.     public List<URL> getURLList(final String key, final List<URL> defaultValue) {
  1246.         return getList(URL.class, key, defaultValue);
  1247.     }

  1248.     @Override
  1249.     protected boolean isEmptyInternal() {
  1250.         return configuration.isEmpty();
  1251.     }

  1252.     @Override
  1253.     protected void setPropertyInternal(final String key, final Object value) {
  1254.         configuration.setProperty(key, value);
  1255.     }
  1256. }