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