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