View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration2;
19  
20  import java.awt.Color;
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.net.URI;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.Calendar;
27  import java.util.Date;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.NoSuchElementException;
32  import java.util.Objects;
33  import java.util.function.Supplier;
34  
35  import org.apache.commons.configuration2.convert.ConversionHandler;
36  import org.apache.commons.configuration2.convert.DefaultConversionHandler;
37  import org.apache.commons.configuration2.ex.ConversionException;
38  import org.apache.commons.lang3.ArrayUtils;
39  import org.apache.commons.lang3.StringUtils;
40  
41  /**
42   * Decorator providing additional getters for any Configuration. This extended Configuration supports more types:
43   * <ul>
44   * <li>{@link java.net.URL}</li>
45   * <li>{@link java.util.Locale}</li>
46   * <li>{@link java.util.Date}</li>
47   * <li>{@link java.util.Calendar}</li>
48   * <li>{@link java.awt.Color}</li>
49   * <li>{@link java.net.InetAddress}</li>
50   * <li>{@code javax.mail.internet.InternetAddress} (requires Javamail in the classpath)</li>
51   * <li>{@code jakarta.mail.internet.InternetAddress} (requires Javamail 2.+ in the classpath)</li>
52   * <li>{@link Enum}</li>
53   * </ul>
54   *
55   * Lists and arrays are available for all types.<br>
56   * Note that this class is only a thin wrapper over functionality already provided by {@link AbstractConfiguration}.
57   * Basically, the generic {@code get()}, and {@code getCollection()} methods are used to actually perform data
58   * conversions.
59   *
60   * <p>
61   * <strong>Example</strong>
62   * </p>
63   *
64   * Configuration file {@code config.properties}:
65   *
66   * <pre>
67   * title.color = #0000FF
68   * remote.host = 192.168.0.53
69   * default.locales = fr,en,de
70   * email.contact = dev@test.org, tester@test.org
71   * </pre>
72   *
73   * Usage:
74   *
75   * <pre>
76   * DataConfiguration config = new DataConfiguration(new PropertiesConfiguration("config.properties"));
77   *
78   * // retrieve a property using a specialized getter
79   * Color color = config.getColor("title.color");
80   *
81   * // retrieve a property using a generic getter
82   * InetAddress host = (InetAddress) config.get(InetAddress.class, "remote.host");
83   * Locale[] locales = (Locale[]) config.getArray(Locale.class, "default.locales");
84   * List contacts = config.getList(InternetAddress.class, "email.contact");
85   * </pre>
86   *
87   * <p>
88   * <strong>Dates</strong>
89   * </p>
90   *
91   * Date objects are expected to be formatted with the pattern {@code yyyy-MM-dd HH:mm:ss}. This default format can be
92   * changed by specifying another format in the getters, or by putting a date format in the configuration under the key
93   * {@code org.apache.commons.configuration.format.date}. Alternatively, the date format can also be specified via the
94   * {@code ConversionHandler} used by a configuration instance:
95   *
96   * <pre>
97   * DefaultConversionHandler handler = new DefaultConversionHandler();
98   * handler.setDateFormat("mm/dd/yyyy");
99   * config.setConversionHandler(handler);
100  * </pre>
101  *
102  * @since 1.1
103  */
104 public 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 }