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