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}