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