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     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
233     */
234    public BigDecimal[] getBigDecimalArray(final String key) {
235        return getBigDecimalArray(key, EMPTY_BIG_DECIMAL_ARRAY);
236    }
237
238    /**
239     * Gets an array of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
240     * an empty array is returned.
241     *
242     * @param key The configuration key.
243     * @param defaultValue the default value, which will be returned if the property is not found
244     * @return The associated BigDecimal array if the key is found.
245     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
246     */
247    public BigDecimal[] getBigDecimalArray(final String key, final BigDecimal... defaultValue) {
248        return get(BigDecimal[].class, key, defaultValue);
249    }
250
251    /**
252     * Gets a list of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object
253     * an empty list is returned.
254     *
255     * @param key The configuration key.
256     * @return The associated BigDecimal list if the key is found.
257     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
258     */
259    public List<BigDecimal> getBigDecimalList(final String key) {
260        return getBigDecimalList(key, new ArrayList<>());
261    }
262
263    /**
264     * Gets a list of BigDecimals associated with the given configuration key. If the key doesn't map to an existing object,
265     * the default value is returned.
266     *
267     * @param key The configuration key.
268     * @param defaultValue The default value.
269     * @return The associated List of BigDecimals.
270     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigDecimals.
271     */
272    public List<BigDecimal> getBigDecimalList(final String key, final List<BigDecimal> defaultValue) {
273        return getList(BigDecimal.class, key, defaultValue);
274    }
275
276    /**
277     * Gets an array of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
278     * an empty array is returned.
279     *
280     * @param key The configuration key.
281     * @return The associated BigInteger array if the key is found.
282     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
283     */
284    public BigInteger[] getBigIntegerArray(final String key) {
285        return getBigIntegerArray(key, EMPTY_BIG_INTEGER_ARRAY);
286    }
287
288    /**
289     * Gets an array of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
290     * an empty array is returned.
291     *
292     * @param key The configuration key.
293     * @param defaultValue the default value, which will be returned if the property is not found
294     * @return The associated BigInteger array if the key is found.
295     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
296     */
297    public BigInteger[] getBigIntegerArray(final String key, final BigInteger... defaultValue) {
298        return get(BigInteger[].class, key, defaultValue);
299    }
300
301    /**
302     * Gets a list of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object
303     * an empty list is returned.
304     *
305     * @param key The configuration key.
306     * @return The associated BigInteger list if the key is found.
307     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
308     */
309    public List<BigInteger> getBigIntegerList(final String key) {
310        return getBigIntegerList(key, new ArrayList<>());
311    }
312
313    /**
314     * Gets a list of BigIntegers associated with the given configuration key. If the key doesn't map to an existing object,
315     * the default value is returned.
316     *
317     * @param key The configuration key.
318     * @param defaultValue The default value.
319     * @return The associated List of BigIntegers.
320     * @throws ConversionException is thrown if the key maps to an object that is not a list of BigIntegers.
321     */
322    public List<BigInteger> getBigIntegerList(final String key, final List<BigInteger> defaultValue) {
323        return getList(BigInteger.class, key, defaultValue);
324    }
325
326    /**
327     * Gets an array of boolean primitives associated with the given configuration key. If the key doesn't map to an existing
328     * object an empty array is returned.
329     *
330     * @param key The configuration key.
331     * @return The associated boolean array if the key is found.
332     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
333     */
334    public boolean[] getBooleanArray(final String key) {
335        return (boolean[]) getArray(Boolean.TYPE, key);
336    }
337
338    /**
339     * Gets an array of boolean primitives associated with the given configuration key. If the key doesn't map to an existing
340     * object, the default value is returned.
341     *
342     * @param key The configuration key.
343     * @param defaultValue The default value.
344     * @return The associated boolean array if the key is found.
345     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
346     */
347    public boolean[] getBooleanArray(final String key, final boolean... defaultValue) {
348        return get(boolean[].class, key, defaultValue);
349    }
350
351    /**
352     * Gets a list of Boolean objects associated with the given configuration key. If the key doesn't map to an existing
353     * object an empty list is returned.
354     *
355     * @param key The configuration key.
356     * @return The associated Boolean list if the key is found.
357     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
358     */
359    public List<Boolean> getBooleanList(final String key) {
360        return getBooleanList(key, new ArrayList<>());
361    }
362
363    /**
364     * Gets a list of Boolean objects associated with the given configuration key. If the key doesn't map to an existing
365     * object, the default value is returned.
366     *
367     * @param key The configuration key.
368     * @param defaultValue The default value.
369     * @return The associated List of Booleans.
370     * @throws ConversionException is thrown if the key maps to an object that is not a list of booleans.
371     */
372    public List<Boolean> getBooleanList(final String key, final List<Boolean> defaultValue) {
373        return getList(Boolean.class, key, defaultValue);
374    }
375
376    /**
377     * Gets an array of byte primitives associated with the given configuration key. If the key doesn't map to an existing
378     * object an empty array is returned.
379     *
380     * @param key The configuration key.
381     * @return The associated byte array if the key is found.
382     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
383     */
384    public byte[] getByteArray(final String key) {
385        return getByteArray(key, ArrayUtils.EMPTY_BYTE_ARRAY);
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     * @param defaultValue the default value, which will be returned if the property is not found
394     * @return The associated byte array if the key is found.
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, final byte... defaultValue) {
398        return get(byte[].class, key, defaultValue);
399    }
400
401    /**
402     * Gets a list of Byte objects associated with the given configuration key. If the key doesn't map to an existing object
403     * an empty list is returned.
404     *
405     * @param key The configuration key.
406     * @return The associated Byte list if the key is found.
407     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
408     */
409    public List<Byte> getByteList(final String key) {
410        return getByteList(key, new ArrayList<>());
411    }
412
413    /**
414     * Gets a list of Byte objects associated with the given configuration key. If the key doesn't map to an existing object,
415     * the default value is returned.
416     *
417     * @param key The configuration key.
418     * @param defaultValue The default value.
419     * @return The associated List of Bytes.
420     * @throws ConversionException is thrown if the key maps to an object that is not a list of bytes.
421     */
422    public List<Byte> getByteList(final String key, final List<Byte> defaultValue) {
423        return getList(Byte.class, key, defaultValue);
424    }
425
426    /**
427     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
428     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
429     * {@link #DEFAULT_DATE_FORMAT} pattern.
430     *
431     * @param key The configuration key.
432     * @return The associated Calendar.
433     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
434     */
435    public Calendar getCalendar(final String key) {
436        return get(Calendar.class, key);
437    }
438
439    /**
440     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
441     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
442     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
443     *
444     * @param key The configuration key.
445     * @param defaultValue The default value.
446     * @return The associated Calendar.
447     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
448     */
449    public Calendar getCalendar(final String key, final Calendar defaultValue) {
450        return getCalendar(key, defaultValue, null);
451    }
452
453    /**
454     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
455     * specified format pattern. If the key doesn't map to an existing object, the default value is returned.
456     *
457     * @param key The configuration key.
458     * @param defaultValue The default value.
459     * @param format The non-localized {@link java.text.DateFormat} pattern.
460     * @return The associated Calendar.
461     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
462     */
463    public Calendar getCalendar(final String key, final Calendar defaultValue, final String format) {
464        return applyTempDateFormat(format, () -> get(Calendar.class, key, defaultValue));
465    }
466
467    /**
468     * Gets a Calendar associated with the given configuration key. If the property is a String, it will be parsed with the
469     * specified format pattern.
470     *
471     * @param key The configuration key.
472     * @param format The non-localized {@link java.text.DateFormat} pattern.
473     * @return The associated Calendar
474     * @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
475     */
476    public Calendar getCalendar(final String key, final String format) {
477        final Calendar value = getCalendar(key, null, format);
478        if (value != null) {
479            return value;
480        }
481        if (isThrowExceptionOnMissing()) {
482            throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
483        }
484        return null;
485    }
486
487    /**
488     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
489     * will be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
490     * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is
491     * returned.
492     *
493     * @param key The configuration key.
494     * @return The associated Calendar array if the key is found.
495     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
496     */
497    public Calendar[] getCalendarArray(final String key) {
498        return getCalendarArray(key, EMPTY_CALENDARD_ARRAY);
499    }
500
501    /**
502     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
503     * will be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
504     * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is
505     * returned.
506     *
507     * @param key The configuration key.
508     * @param defaultValue the default value, which will be returned if the property is not found
509     * @return The associated Calendar array if the key is found.
510     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
511     */
512    public Calendar[] getCalendarArray(final String key, final Calendar... defaultValue) {
513        return getCalendarArray(key, defaultValue, null);
514    }
515
516    /**
517     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
518     * will be parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
519     * returned.
520     *
521     * @param key The configuration key.
522     * @param defaultValue The default value.
523     * @param format The non-localized {@link java.text.DateFormat} pattern.
524     * @return The associated Calendar array if the key is found.
525     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
526     */
527    public Calendar[] getCalendarArray(final String key, final Calendar[] defaultValue, final String format) {
528        return applyTempDateFormat(format, () -> get(Calendar[].class, key, defaultValue));
529    }
530
531    /**
532     * Gets an array of Calendars associated with the given configuration key. If the property is a list of Strings, they
533     * will be parsed with the specified format pattern. If the key doesn't map to an existing object an empty array is
534     * returned.
535     *
536     * @param key The configuration key.
537     * @param format The non-localized {@link java.text.DateFormat} pattern.
538     * @return The associated Calendar array if the key is found.
539     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
540     */
541    public Calendar[] getCalendarArray(final String key, final String format) {
542        return getCalendarArray(key, EMPTY_CALENDARD_ARRAY, format);
543    }
544
545    /**
546     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
547     * be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with
548     * the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty list is returned.
549     *
550     * @param key The configuration key.
551     * @return The associated Calendar list if the key is found.
552     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
553     */
554    public List<Calendar> getCalendarList(final String key) {
555        return getCalendarList(key, new ArrayList<>());
556    }
557
558    /**
559     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
560     * be parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with
561     * the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is
562     * returned.
563     *
564     * @param key The configuration key.
565     * @param defaultValue The default value.
566     * @return The associated Calendar list if the key is found.
567     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
568     */
569    public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue) {
570        return getCalendarList(key, defaultValue, null);
571    }
572
573    /**
574     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
575     * be parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
576     * returned.
577     *
578     * @param key The configuration key.
579     * @param defaultValue The default value.
580     * @param format The non-localized {@link java.text.DateFormat} pattern.
581     * @return The associated Calendar list if the key is found.
582     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
583     */
584    public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue, final String format) {
585        return applyTempDateFormat(format, () -> getList(Calendar.class, key, defaultValue));
586    }
587
588    /**
589     * Gets a list of Calendars associated with the given configuration key. If the property is a list of Strings, they will
590     * be parsed with the specified format pattern. If the key doesn't map to an existing object an empty list is returned.
591     *
592     * @param key The configuration key.
593     * @param format The non-localized {@link java.text.DateFormat} pattern.
594     * @return The associated Calendar list if the key is found.
595     * @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
596     */
597    public List<Calendar> getCalendarList(final String key, final String format) {
598        return getCalendarList(key, new ArrayList<>(), format);
599    }
600
601    /**
602     * Gets a Color associated with the given configuration key.
603     *
604     * @param key The configuration key.
605     * @return The associated Color.
606     * @throws ConversionException is thrown if the key maps to an object that is not a Color.
607     */
608    public Color getColor(final String key) {
609        return get(Color.class, key);
610    }
611
612    /**
613     * Gets a Color associated with the given configuration key. If the key doesn't map to an existing object, the default
614     * value is returned.
615     *
616     * @param key The configuration key.
617     * @param defaultValue The default value.
618     * @return The associated Color.
619     * @throws ConversionException is thrown if the key maps to an object that is not a Color.
620     */
621    public Color getColor(final String key, final Color defaultValue) {
622        return get(Color.class, key, defaultValue);
623    }
624
625    /**
626     * Gets an array of Colors associated with the given configuration key. If the key doesn't map to an existing object an
627     * empty array is returned.
628     *
629     * @param key The configuration key.
630     * @return The associated Color array if the key is found.
631     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
632     */
633    public Color[] getColorArray(final String key) {
634        return getColorArray(key, EMPTY_COLOR_ARRAY);
635    }
636
637    /**
638     * Gets an array of Colors associated with the given configuration key. If the key doesn't map to an existing object an
639     * empty array is returned.
640     *
641     * @param key The configuration key.
642     * @param defaultValue the default value, which will be returned if the property is not found
643     * @return The associated Color array if the key is found.
644     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
645     */
646    public Color[] getColorArray(final String key, final Color... defaultValue) {
647        return get(Color[].class, key, defaultValue);
648    }
649
650    /**
651     * Gets a list of Colors associated with the given configuration key. If the key doesn't map to an existing object an
652     * empty list is returned.
653     *
654     * @param key The configuration key.
655     * @return The associated Color list if the key is found.
656     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
657     */
658    public List<Color> getColorList(final String key) {
659        return getColorList(key, new ArrayList<>());
660    }
661
662    /**
663     * Gets a list of Colors associated with the given configuration key. If the key doesn't map to an existing object, the
664     * default value is returned.
665     *
666     * @param key The configuration key.
667     * @param defaultValue The default value.
668     * @return The associated List of Colors.
669     * @throws ConversionException is thrown if the key maps to an object that is not a list of Colors.
670     */
671    public List<Color> getColorList(final String key, final List<Color> defaultValue) {
672        return getList(Color.class, key, defaultValue);
673    }
674
675    /**
676     * Gets the configuration decorated by this DataConfiguration.
677     *
678     * @return the wrapped configuration
679     */
680    public Configuration getConfiguration() {
681        return configuration;
682    }
683
684    /**
685     * {@inheritDoc} This implementation returns the special conversion handler used by this configuration instance.
686     */
687    @Override
688    public ConversionHandler getConversionHandler() {
689        return dataConversionHandler;
690    }
691
692    /**
693     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
694     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
695     * {@link #DEFAULT_DATE_FORMAT} pattern.
696     *
697     * @param key The configuration key.
698     * @return The associated Date.
699     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
700     */
701    public Date getDate(final String key) {
702        return get(Date.class, key);
703    }
704
705    /**
706     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
707     * format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
708     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
709     *
710     * @param key The configuration key.
711     * @param defaultValue The default value.
712     * @return The associated Date.
713     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
714     */
715    public Date getDate(final String key, final Date defaultValue) {
716        return getDate(key, defaultValue, null);
717    }
718
719    /**
720     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
721     * specified format pattern. If the key doesn't map to an existing object, the default value is returned.
722     *
723     * @param key The configuration key.
724     * @param defaultValue The default value.
725     * @param format The non-localized {@link java.text.DateFormat} pattern.
726     * @return The associated Date.
727     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
728     */
729    public Date getDate(final String key, final Date defaultValue, final String format) {
730        return applyTempDateFormat(format, () -> get(Date.class, key, defaultValue));
731    }
732
733    /**
734     * Gets a Date associated with the given configuration key. If the property is a String, it will be parsed with the
735     * specified format pattern.
736     *
737     * @param key The configuration key.
738     * @param format The non-localized {@link java.text.DateFormat} pattern.
739     * @return The associated Date
740     * @throws ConversionException is thrown if the key maps to an object that is not a Date.
741     */
742    public Date getDate(final String key, final String format) {
743        final Date value = getDate(key, null, format);
744        if (value != null) {
745            return value;
746        }
747        if (isThrowExceptionOnMissing()) {
748            throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
749        }
750        return null;
751    }
752
753    /**
754     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
755     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
756     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is returned.
757     *
758     * @param key The configuration key.
759     * @return The associated Date array if the key is found.
760     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
761     */
762    public Date[] getDateArray(final String key) {
763        return getDateArray(key, EMPTY_DATE_ARRAY);
764    }
765
766    /**
767     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
768     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
769     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object an empty array is returned.
770     *
771     * @param key The configuration key.
772     * @param defaultValue the default value, which will be returned if the property is not found
773     * @return The associated Date array if the key is found.
774     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
775     */
776    public Date[] getDateArray(final String key, final Date... defaultValue) {
777        return getDateArray(key, defaultValue, null);
778    }
779
780    /**
781     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
782     * parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
783     * returned.
784     *
785     * @param key The configuration key.
786     * @param defaultValue The default value.
787     * @param format The non-localized {@link java.text.DateFormat} pattern.
788     * @return The associated Date array if the key is found.
789     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
790     */
791    public Date[] getDateArray(final String key, final Date[] defaultValue, final String format) {
792        return applyTempDateFormat(format, () -> get(Date[].class, key, defaultValue));
793    }
794
795    /**
796     * Gets an array of Dates associated with the given configuration key. If the property is a list of Strings, they will be
797     * parsed with the specified format pattern. If the key doesn't map to an existing object an empty array is returned.
798     *
799     * @param key The configuration key.
800     * @param format The non-localized {@link java.text.DateFormat} pattern.
801     * @return The associated Date array if the key is found.
802     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
803     */
804    public Date[] getDateArray(final String key, final String format) {
805        return getDateArray(key, EMPTY_DATE_ARRAY, format);
806    }
807
808    /**
809     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
810     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
811     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, a new list is returned.
812     *
813     * @param key The configuration key.
814     * @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.
815     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
816     */
817    public List<Date> getDateList(final String key) {
818        return getDateList(key, new ArrayList<>());
819    }
820
821    /**
822     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
823     * parsed with the format defined by the user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
824     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an existing object, the default value is returned.
825     *
826     * @param key The configuration key.
827     * @param defaultValue The default value.
828     * @return The associated Date list if the key is found.
829     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
830     */
831    public List<Date> getDateList(final String key, final List<Date> defaultValue) {
832        return getDateList(key, defaultValue, null);
833    }
834
835    /**
836     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
837     * parsed with the specified format pattern. If the key doesn't map to an existing object, the default value is
838     * returned.
839     *
840     * @param key The configuration key.
841     * @param defaultValue The default value.
842     * @param format The non-localized {@link java.text.DateFormat} pattern.
843     * @return The associated Date list if the key is found.
844     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
845     */
846    public List<Date> getDateList(final String key, final List<Date> defaultValue, final String format) {
847        return applyTempDateFormat(format, () -> getList(Date.class, key, defaultValue));
848    }
849
850    /**
851     * Gets a list of Dates associated with the given configuration key. If the property is a list of Strings, they will be
852     * parsed with the specified format pattern. If the key doesn't map to an existing object an empty list is returned.
853     *
854     * @param key The configuration key.
855     * @param format The non-localized {@link java.text.DateFormat} pattern.
856     * @return The associated Date list if the key is found.
857     * @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
858     */
859    public List<Date> getDateList(final String key, final String format) {
860        return getDateList(key, new ArrayList<>(), format);
861    }
862
863    /**
864     * Gets the date format specified by the user in the DATE_FORMAT_KEY property, or the default format otherwise.
865     *
866     * @return the default date format
867     */
868    private String getDefaultDateFormat() {
869        return getString(DATE_FORMAT_KEY, DEFAULT_DATE_FORMAT);
870    }
871
872    /**
873     * Gets an array of double primitives associated with the given configuration key. If the key doesn't map to an existing
874     * object an empty array is returned.
875     *
876     * @param key The configuration key.
877     * @return The associated double array if the key is found.
878     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
879     */
880    public double[] getDoubleArray(final String key) {
881        return getDoubleArray(key, ArrayUtils.EMPTY_DOUBLE_ARRAY);
882    }
883
884    /**
885     * Gets an array of double primitives associated with the given configuration key. If the key doesn't map to an existing
886     * object an empty array is returned.
887     *
888     * @param key The configuration key.
889     * @param defaultValue the default value, which will be returned if the property is not found
890     * @return The associated double array if the key is found.
891     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
892     */
893    public double[] getDoubleArray(final String key, final double... defaultValue) {
894        return get(double[].class, key, defaultValue);
895    }
896
897    /**
898     * Gets a list of Double objects associated with the given configuration key. If the key doesn't map to an existing
899     * object an empty list is returned.
900     *
901     * @param key The configuration key.
902     * @return The associated Double list if the key is found.
903     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
904     */
905    public List<Double> getDoubleList(final String key) {
906        return getDoubleList(key, new ArrayList<>());
907    }
908
909    /**
910     * Gets a list of Double objects associated with the given configuration key. If the key doesn't map to an existing
911     * object, the default value is returned.
912     *
913     * @param key The configuration key.
914     * @param defaultValue The default value.
915     * @return The associated List of Doubles.
916     * @throws ConversionException is thrown if the key maps to an object that is not a list of doubles.
917     */
918    public List<Double> getDoubleList(final String key, final List<Double> defaultValue) {
919        return getList(Double.class, key, defaultValue);
920    }
921
922    /**
923     * Gets an array of float primitives associated with the given configuration key. If the key doesn't map to an existing
924     * object an empty array is returned.
925     *
926     * @param key The configuration key.
927     * @return The associated float array if the key is found.
928     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
929     */
930    public float[] getFloatArray(final String key) {
931        return getFloatArray(key, ArrayUtils.EMPTY_FLOAT_ARRAY);
932    }
933
934    /**
935     * Gets an array of float primitives associated with the given configuration key. If the key doesn't map to an existing
936     * object an empty array is returned.
937     *
938     * @param key The configuration key.
939     * @param defaultValue the default value, which will be returned if the property is not found
940     * @return The associated float array if the key is found.
941     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
942     */
943    public float[] getFloatArray(final String key, final float... defaultValue) {
944        return get(float[].class, key, defaultValue);
945    }
946
947    /**
948     * Gets a list of Float objects associated with the given configuration key. If the key doesn't map to an existing object
949     * an empty list is returned.
950     *
951     * @param key The configuration key.
952     * @return The associated Float list if the key is found.
953     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
954     */
955    public List<Float> getFloatList(final String key) {
956        return getFloatList(key, new ArrayList<>());
957    }
958
959    /**
960     * Gets a list of Float objects associated with the given configuration key. If the key doesn't map to an existing
961     * object, the default value is returned.
962     *
963     * @param key The configuration key.
964     * @param defaultValue The default value.
965     * @return The associated List of Floats.
966     * @throws ConversionException is thrown if the key maps to an object that is not a list of floats.
967     */
968    public List<Float> getFloatList(final String key, final List<Float> defaultValue) {
969        return getList(Float.class, key, defaultValue);
970    }
971
972    /**
973     * Gets an array of int primitives associated with the given configuration key. If the key doesn't map to an existing
974     * object an empty array is returned.
975     *
976     * @param key The configuration key.
977     * @return The associated int array if the key is found.
978     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
979     */
980    public int[] getIntArray(final String key) {
981        return getIntArray(key, ArrayUtils.EMPTY_INT_ARRAY);
982    }
983
984    /**
985     * Gets an array of int primitives associated with the given configuration key. If the key doesn't map to an existing
986     * object an empty array is returned.
987     *
988     * @param key The configuration key.
989     * @param defaultValue the default value, which will be returned if the property is not found
990     * @return The associated int array if the key is found.
991     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
992     */
993    public int[] getIntArray(final String key, final int... defaultValue) {
994        return get(int[].class, key, defaultValue);
995    }
996
997    /**
998     * Gets a list of Integer objects associated with the given configuration key. If the key doesn't map to an existing
999     * object an empty list is returned.
1000     *
1001     * @param key The configuration key.
1002     * @return The associated Integer list if the key is found.
1003     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
1004     */
1005    public List<Integer> getIntegerList(final String key) {
1006        return getIntegerList(key, new ArrayList<>());
1007    }
1008
1009    /**
1010     * Gets a list of Integer objects associated with the given configuration key. If the key doesn't map to an existing
1011     * object, the default value is returned.
1012     *
1013     * @param key The configuration key.
1014     * @param defaultValue The default value.
1015     * @return The associated List of Integers.
1016     * @throws ConversionException is thrown if the key maps to an object that is not a list of integers.
1017     */
1018    public List<Integer> getIntegerList(final String key, final List<Integer> defaultValue) {
1019        return getList(Integer.class, key, defaultValue);
1020    }
1021
1022    @Override
1023    protected Iterator<String> getKeysInternal() {
1024        return configuration.getKeys();
1025    }
1026
1027    /**
1028     * Gets a Locale associated with the given configuration key.
1029     *
1030     * @param key The configuration key.
1031     * @return The associated Locale.
1032     * @throws ConversionException is thrown if the key maps to an object that is not a Locale.
1033     */
1034    public Locale getLocale(final String key) {
1035        return get(Locale.class, key);
1036    }
1037
1038    /**
1039     * Gets a Locale associated with the given configuration key. If the key doesn't map to an existing object, the default
1040     * value is returned.
1041     *
1042     * @param key The configuration key.
1043     * @param defaultValue The default value.
1044     * @return The associated Locale.
1045     * @throws ConversionException is thrown if the key maps to an object that is not a Locale.
1046     */
1047    public Locale getLocale(final String key, final Locale defaultValue) {
1048        return get(Locale.class, key, defaultValue);
1049    }
1050
1051    /**
1052     * Gets an array of Locales associated with the given configuration key. If the key doesn't map to an existing object an
1053     * empty array is returned.
1054     *
1055     * @param key The configuration key.
1056     * @return The associated Locale array if the key is found.
1057     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
1058     */
1059    public Locale[] getLocaleArray(final String key) {
1060        return getLocaleArray(key, EMPTY_LOCALE_ARRAY);
1061    }
1062
1063    /**
1064     * Gets an array of Locales associated with the given configuration key. If the key doesn't map to an existing object an
1065     * empty array is returned.
1066     *
1067     * @param key The configuration key.
1068     * @param defaultValue the default value, which will be returned if the property is not found
1069     * @return The associated Locale array if the key is found.
1070     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
1071     */
1072    public Locale[] getLocaleArray(final String key, final Locale... defaultValue) {
1073        return get(Locale[].class, key, defaultValue);
1074    }
1075
1076    /**
1077     * Gets a list of Locales associated with the given configuration key. If the key doesn't map to an existing object an
1078     * empty list is returned.
1079     *
1080     * @param key The configuration key.
1081     * @return The associated Locale list if the key is found.
1082     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
1083     */
1084    public List<Locale> getLocaleList(final String key) {
1085        return getLocaleList(key, new ArrayList<>());
1086    }
1087
1088    /**
1089     * Gets a list of Locales associated with the given configuration key. If the key doesn't map to an existing object, the
1090     * default value is returned.
1091     *
1092     * @param key The configuration key.
1093     * @param defaultValue The default value.
1094     * @return The associated List of Locales.
1095     * @throws ConversionException is thrown if the key maps to an object that is not a list of Locales.
1096     */
1097    public List<Locale> getLocaleList(final String key, final List<Locale> defaultValue) {
1098        return getList(Locale.class, key, defaultValue);
1099    }
1100
1101    /**
1102     * Gets an array of long primitives associated with the given configuration key. If the key doesn't map to an existing
1103     * object an empty array is returned.
1104     *
1105     * @param key The configuration key.
1106     * @return The associated long array if the key is found.
1107     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
1108     */
1109    public long[] getLongArray(final String key) {
1110        return getLongArray(key, ArrayUtils.EMPTY_LONG_ARRAY);
1111    }
1112
1113    /**
1114     * Gets an array of long primitives associated with the given configuration key. If the key doesn't map to an existing
1115     * object an empty array is returned.
1116     *
1117     * @param key The configuration key.
1118     * @param defaultValue the default value, which will be returned if the property is not found
1119     * @return The associated long array if the key is found.
1120     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
1121     */
1122    public long[] getLongArray(final String key, final long... defaultValue) {
1123        return get(long[].class, key, defaultValue);
1124    }
1125
1126    /**
1127     * Gets a list of Long objects associated with the given configuration key. If the key doesn't map to an existing object
1128     * an empty list is returned.
1129     *
1130     * @param key The configuration key.
1131     * @return The associated Long list if the key is found.
1132     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
1133     */
1134    public List<Long> getLongList(final String key) {
1135        return getLongList(key, new ArrayList<>());
1136    }
1137
1138    /**
1139     * Gets a list of Long objects associated with the given configuration key. If the key doesn't map to an existing object,
1140     * the default value is returned.
1141     *
1142     * @param key The configuration key.
1143     * @param defaultValue The default value.
1144     * @return The associated List of Longs.
1145     * @throws ConversionException is thrown if the key maps to an object that is not a list of longs.
1146     */
1147    public List<Long> getLongList(final String key, final List<Long> defaultValue) {
1148        return getList(Long.class, key, defaultValue);
1149    }
1150
1151    /**
1152     * Gets the original conversion handler set for this configuration. If this is not a
1153     * {@code DefaultConversionHandler}, result is <strong>null</strong>.
1154     *
1155     * @return the original conversion handler or <strong>null</strong>
1156     */
1157    private DefaultConversionHandler getOriginalConversionHandler() {
1158        final ConversionHandler handler = super.getConversionHandler();
1159        return (DefaultConversionHandler) (handler instanceof DefaultConversionHandler ? handler : null);
1160    }
1161
1162    @Override
1163    protected Object getPropertyInternal(final String key) {
1164        return configuration.getProperty(key);
1165    }
1166
1167    /**
1168     * Gets an array of short primitives associated with the given configuration key. If the key doesn't map to an existing
1169     * object an empty array is returned.
1170     *
1171     * @param key The configuration key.
1172     * @return The associated short array if the key is found.
1173     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
1174     */
1175    public short[] getShortArray(final String key) {
1176        return getShortArray(key, ArrayUtils.EMPTY_SHORT_ARRAY);
1177    }
1178
1179    /**
1180     * Gets an array of short primitives associated with the given configuration key. If the key doesn't map to an existing
1181     * object an empty array is returned.
1182     *
1183     * @param key The configuration key.
1184     * @param defaultValue the default value, which will be returned if the property is not found
1185     * @return The associated short array if the key is found.
1186     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
1187     */
1188    public short[] getShortArray(final String key, final short... defaultValue) {
1189        return get(short[].class, key, defaultValue);
1190    }
1191
1192    /**
1193     * Gets a list of Short objects associated with the given configuration key. If the key doesn't map to an existing object
1194     * an empty list is returned.
1195     *
1196     * @param key The configuration key.
1197     * @return The associated Short list if the key is found.
1198     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
1199     */
1200    public List<Short> getShortList(final String key) {
1201        return getShortList(key, new ArrayList<>());
1202    }
1203
1204    /**
1205     * Gets a list of Short objects associated with the given configuration key. If the key doesn't map to an existing
1206     * object, the default value is returned.
1207     *
1208     * @param key The configuration key.
1209     * @param defaultValue The default value.
1210     * @return The associated List of Shorts.
1211     * @throws ConversionException is thrown if the key maps to an object that is not a list of shorts.
1212     */
1213    public List<Short> getShortList(final String key, final List<Short> defaultValue) {
1214        return getList(Short.class, key, defaultValue);
1215    }
1216
1217    /**
1218     * Gets an URI associated with the given configuration key.
1219     *
1220     * @param key The configuration key.
1221     * @return The associated URI.
1222     * @throws ConversionException is thrown if the key maps to an object that is not an URI.
1223     */
1224    public URI getURI(final String key) {
1225        return get(URI.class, key);
1226    }
1227
1228    /**
1229     * Gets an URI associated with the given configuration key. If the key doesn't map to an existing object, the default
1230     * value is returned.
1231     *
1232     * @param key The configuration key.
1233     * @param defaultValue The default value.
1234     * @return The associated URI.
1235     * @throws ConversionException is thrown if the key maps to an object that is not an URI.
1236     */
1237    public URI getURI(final String key, final URI defaultValue) {
1238        return get(URI.class, key, defaultValue);
1239    }
1240
1241    /**
1242     * Gets an array of URIs associated with the given configuration key. If the key doesn't map to an existing object an
1243     * empty array is returned.
1244     *
1245     * @param key The configuration key.
1246     * @return The associated URI array if the key is found.
1247     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
1248     */
1249    public URI[] getURIArray(final String key) {
1250        return getURIArray(key, EMPTY_URI_ARRAY);
1251    }
1252
1253    /**
1254     * Gets an array of URIs associated with the given configuration key. If the key doesn't map to an existing object an
1255     * empty array is returned.
1256     *
1257     * @param key The configuration key.
1258     * @param defaultValue the default value, which will be returned if the property is not found
1259     * @return The associated URI array if the key is found.
1260     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
1261     */
1262    public URI[] getURIArray(final String key, final URI... defaultValue) {
1263        return get(URI[].class, key, defaultValue);
1264    }
1265
1266    /**
1267     * Gets a list of URIs associated with the given configuration key. If the key doesn't map to an existing object an empty
1268     * list is returned.
1269     *
1270     * @param key The configuration key.
1271     * @return The associated URI list if the key is found.
1272     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
1273     */
1274    public List<URI> getURIList(final String key) {
1275        return getURIList(key, new ArrayList<>());
1276    }
1277
1278    /**
1279     * Gets a list of URIs associated with the given configuration key. If the key doesn't map to an existing object, the
1280     * default value is returned.
1281     *
1282     * @param key The configuration key.
1283     * @param defaultValue The default value.
1284     * @return The associated List of URIs.
1285     * @throws ConversionException is thrown if the key maps to an object that is not a list of URIs.
1286     */
1287    public List<URI> getURIList(final String key, final List<URI> defaultValue) {
1288        return getList(URI.class, key, defaultValue);
1289    }
1290
1291    /**
1292     * Gets an URL associated with the given configuration key.
1293     *
1294     * @param key The configuration key.
1295     * @return The associated URL.
1296     * @throws ConversionException is thrown if the key maps to an object that is not an URL.
1297     */
1298    public URL getURL(final String key) {
1299        return get(URL.class, key);
1300    }
1301
1302    /**
1303     * Gets an URL associated with the given configuration key. If the key doesn't map to an existing object, the default
1304     * value is returned.
1305     *
1306     * @param key The configuration key.
1307     * @param defaultValue The default value.
1308     * @return The associated URL.
1309     * @throws ConversionException is thrown if the key maps to an object that is not an URL.
1310     */
1311    public URL getURL(final String key, final URL defaultValue) {
1312        return get(URL.class, key, defaultValue);
1313    }
1314
1315    /**
1316     * Gets an array of URLs associated with the given configuration key. If the key doesn't map to an existing object an
1317     * empty array is returned.
1318     *
1319     * @param key The configuration key.
1320     * @return The associated URL array if the key is found.
1321     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
1322     */
1323    public URL[] getURLArray(final String key) {
1324        return getURLArray(key, EMPTY_URL_ARRAY);
1325    }
1326
1327    /**
1328     * Gets an array of URLs associated with the given configuration key. If the key doesn't map to an existing object an
1329     * empty array is returned.
1330     *
1331     * @param key The configuration key.
1332     * @param defaultValue the default value, which will be returned if the property is not found
1333     * @return The associated URL array if the key is found.
1334     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
1335     */
1336    public URL[] getURLArray(final String key, final URL... defaultValue) {
1337        return get(URL[].class, key, defaultValue);
1338    }
1339
1340    /**
1341     * Gets a list of URLs associated with the given configuration key. If the key doesn't map to an existing object an empty
1342     * list is returned.
1343     *
1344     * @param key The configuration key.
1345     * @return The associated URL list if the key is found.
1346     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
1347     */
1348    public List<URL> getURLList(final String key) {
1349        return getURLList(key, new ArrayList<>());
1350    }
1351
1352    /**
1353     * Gets a list of URLs associated with the given configuration key. If the key doesn't map to an existing object, the
1354     * default value is returned.
1355     *
1356     * @param key The configuration key.
1357     * @param defaultValue The default value.
1358     * @return The associated List of URLs.
1359     * @throws ConversionException is thrown if the key maps to an object that is not a list of URLs.
1360     */
1361    public List<URL> getURLList(final String key, final List<URL> defaultValue) {
1362        return getList(URL.class, key, defaultValue);
1363    }
1364
1365    @Override
1366    protected boolean isEmptyInternal() {
1367        return configuration.isEmpty();
1368    }
1369
1370    @Override
1371    protected void setPropertyInternal(final String key, final Object value) {
1372        configuration.setProperty(key, value);
1373    }
1374}