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