001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.configuration2;
019
020import java.awt.Color;
021import java.math.BigDecimal;
022import java.math.BigInteger;
023import java.net.URI;
024import java.net.URL;
025import java.util.ArrayList;
026import java.util.Calendar;
027import java.util.Date;
028import java.util.Iterator;
029import java.util.List;
030import java.util.Locale;
031import java.util.NoSuchElementException;
032
033import org.apache.commons.configuration2.convert.ConversionHandler;
034import org.apache.commons.configuration2.convert.DefaultConversionHandler;
035import org.apache.commons.configuration2.ex.ConversionException;
036import org.apache.commons.lang3.StringUtils;
037
038/**
039 * Decorator providing additional getters for any Configuration. This extended
040 * Configuration supports more types:
041 * <ul>
042 *   <li>{@link java.net.URL}</li>
043 *   <li>{@link java.util.Locale}</li>
044 *   <li>{@link java.util.Date}</li>
045 *   <li>{@link java.util.Calendar}</li>
046 *   <li>{@link java.awt.Color}</li>
047 *   <li>{@link java.net.InetAddress}</li>
048 *   <li>{@code javax.mail.internet.InternetAddress} (requires Javamail in the classpath)</li>
049 *   <li>{@link java.lang.Enum} (Java 5 enumeration types)</li>
050 * </ul>
051 *
052 * Lists and arrays are available for all types.<br>
053 * Note that this class is only a thin wrapper over functionality already
054 * provided by {@link AbstractConfiguration}. Basically, the generic
055 * {@code get()}, and {@code getCollection()} methods are
056 * used to actually perform data conversions.
057 *
058 * <p><strong>Example</strong></p>
059 *
060 * Configuration file {@code config.properties}:
061 * <pre>
062 * title.color = #0000FF
063 * remote.host = 192.168.0.53
064 * default.locales = fr,en,de
065 * email.contact = ebourg@apache.org, tester@test.org
066 * </pre>
067 *
068 * Usage:
069 *
070 * <pre>
071 * DataConfiguration config = new DataConfiguration(new PropertiesConfiguration("config.properties"));
072 *
073 * // retrieve a property using a specialized getter
074 * Color color = config.getColor("title.color");
075 *
076 * // retrieve a property using a generic getter
077 * InetAddress host = (InetAddress) config.get(InetAddress.class, "remote.host");
078 * Locale[] locales = (Locale[]) config.getArray(Locale.class, "default.locales");
079 * List contacts = config.getList(InternetAddress.class, "email.contact");
080 * </pre>
081 *
082 * <p><strong>Dates</strong></p>
083 *
084 * Date objects are expected to be formatted with the pattern {@code yyyy-MM-dd HH:mm:ss}.
085 * This default format can be changed by specifying another format in the
086 * getters, or by putting a date format in the configuration under the key
087 * {@code org.apache.commons.configuration.format.date}. Alternatively, the
088 * date format can also be specified via the {@code ConversionHandler} used
089 * by a configuration instance:
090 *
091 * <pre>
092 * DefaultConversionHandler handler = new DefaultConversionHandler();
093 * handler.setDateFormat("mm/dd/yyyy");
094 * config.setConversionHandler(handler);
095 * </pre>
096 *
097 * @since 1.1
098 */
099public class DataConfiguration extends AbstractConfiguration
100{
101    /** The key of the property storing the user defined date format. */
102    public static final String DATE_FORMAT_KEY = "org.apache.commons.configuration.format.date";
103
104    /** The default format for dates. */
105    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
106
107    /** Stores temporary date formats. */
108    private static final ThreadLocal<String> TEMP_DATE_FORMAT = new ThreadLocal<>();
109
110    /** Stores the wrapped configuration.*/
111    private final Configuration configuration;
112
113    /** A special conversion handler object used by this configuration. */
114    private final ConversionHandler dataConversionHandler;
115
116    /**
117     * Creates a new instance of {@code DataConfiguration} and sets the
118     * wrapped configuration.
119     *
120     * @param configuration the wrapped configuration
121     */
122    public DataConfiguration(final Configuration configuration)
123    {
124        this.configuration = configuration;
125        dataConversionHandler = new DataConversionHandler();
126    }
127
128    /**
129     * Return the configuration decorated by this DataConfiguration.
130     *
131     * @return the wrapped configuration
132     */
133    public Configuration getConfiguration()
134    {
135        return configuration;
136    }
137
138    /**
139     * {@inheritDoc} This implementation returns the special conversion handler
140     * used by this configuration instance.
141     */
142    @Override
143    public ConversionHandler getConversionHandler()
144    {
145        return dataConversionHandler;
146    }
147
148    @Override
149    protected Object getPropertyInternal(final String key)
150    {
151        return configuration.getProperty(key);
152    }
153
154    @Override
155    protected void addPropertyInternal(final String key, final Object obj)
156    {
157        configuration.addProperty(key, obj);
158    }
159
160    @Override
161    protected void addPropertyDirect(final String key, final Object value)
162    {
163        if (configuration instanceof AbstractConfiguration)
164        {
165            ((AbstractConfiguration) configuration).addPropertyDirect(key, value);
166        }
167        else
168        {
169            configuration.addProperty(key, value);
170        }
171    }
172
173    @Override
174    protected boolean isEmptyInternal()
175    {
176        return configuration.isEmpty();
177    }
178
179    @Override
180    protected boolean containsKeyInternal(final String key)
181    {
182        return configuration.containsKey(key);
183    }
184
185    @Override
186    protected void clearPropertyDirect(final String key)
187    {
188        configuration.clearProperty(key);
189    }
190
191    @Override
192    protected void setPropertyInternal(final String key, final Object value)
193    {
194        configuration.setProperty(key, value);
195    }
196
197    @Override
198    protected Iterator<String> getKeysInternal()
199    {
200        return configuration.getKeys();
201    }
202
203    /**
204     * Get a list of Boolean objects associated with the given
205     * configuration key. If the key doesn't map to an existing object
206     * an empty list is returned.
207     *
208     * @param key The configuration key.
209     * @return The associated Boolean list if the key is found.
210     *
211     * @throws ConversionException is thrown if the key maps to an
212     *         object that is not a list of booleans.
213     */
214    public List<Boolean> getBooleanList(final String key)
215    {
216        return getBooleanList(key, new ArrayList<Boolean>());
217    }
218
219    /**
220     * Get a list of Boolean objects associated with the given
221     * configuration key. If the key doesn't map to an existing object,
222     * the default value is returned.
223     *
224     * @param key The configuration key.
225     * @param defaultValue The default value.
226     * @return The associated List of Booleans.
227     *
228     * @throws ConversionException is thrown if the key maps to an
229     *         object that is not a list of booleans.
230     */
231    public List<Boolean> getBooleanList(final String key, final List<Boolean> defaultValue)
232    {
233         return getList(Boolean.class, key, defaultValue);
234    }
235
236    /**
237     * Get an array of boolean primitives associated with the given
238     * configuration key. If the key doesn't map to an existing object
239     * an empty array is returned.
240     *
241     * @param key The configuration key.
242     * @return The associated boolean array if the key is found.
243     *
244     * @throws ConversionException is thrown if the key maps to an
245     *         object that is not a list of booleans.
246     */
247    public boolean[] getBooleanArray(final String key)
248    {
249        return (boolean[]) getArray(Boolean.TYPE, key);
250    }
251
252    /**
253     * Get an array of boolean primitives associated with the given
254     * configuration key. If the key doesn't map to an existing object,
255     * the default value is returned.
256     *
257     * @param key          The configuration key.
258     * @param defaultValue The default value.
259     * @return The associated boolean array if the key is found.
260     *
261     * @throws ConversionException is thrown if the key maps to an
262     *         object that is not a list of booleans.
263     */
264    public boolean[] getBooleanArray(final String key, final boolean[] defaultValue)
265    {
266        return get(boolean[].class, key, defaultValue);
267    }
268
269    /**
270     * Get a list of Byte objects associated with the given configuration key.
271     * If the key doesn't map to an existing object an empty list is returned.
272     *
273     * @param key The configuration key.
274     * @return The associated Byte list if the key is found.
275     *
276     * @throws ConversionException is thrown if the key maps to an
277     *         object that is not a list of bytes.
278     */
279    public List<Byte> getByteList(final String key)
280    {
281        return getByteList(key, new ArrayList<Byte>());
282    }
283
284    /**
285     * Get a list of Byte objects associated with the given configuration key.
286     * If the key doesn't map to an existing object, the default value is
287     * returned.
288     *
289     * @param key The configuration key.
290     * @param defaultValue The default value.
291     * @return The associated List of Bytes.
292     *
293     * @throws ConversionException is thrown if the key maps to an
294     *         object that is not a list of bytes.
295     */
296    public List<Byte> getByteList(final String key, final List<Byte> defaultValue)
297    {
298        return getList(Byte.class, key, defaultValue);
299    }
300
301    /**
302     * Get an array of byte primitives associated with the given
303     * configuration key. If the key doesn't map to an existing object
304     * an empty array is returned.
305     *
306     * @param key The configuration key.
307     * @return The associated byte array if the key is found.
308     *
309     * @throws ConversionException is thrown if the key maps to an
310     *         object that is not a list of bytes.
311     */
312    public byte[] getByteArray(final String key)
313    {
314        return getByteArray(key, new byte[0]);
315    }
316
317    /**
318     * Get an array of byte primitives associated with the given
319     * configuration key. If the key doesn't map to an existing object
320     * an empty array is returned.
321     *
322     * @param key The configuration key.
323     * @param defaultValue the default value, which will be returned if the property is not found
324     * @return The associated byte array if the key is found.
325     *
326     * @throws ConversionException is thrown if the key maps to an
327     *         object that is not a list of bytes.
328     */
329    public byte[] getByteArray(final String key, final byte[] defaultValue)
330    {
331        return get(byte[].class, key, defaultValue);
332    }
333
334    /**
335     * Get a list of Short objects associated with the given configuration key.
336     * If the key doesn't map to an existing object an empty list is returned.
337     *
338     * @param key The configuration key.
339     * @return The associated Short list if the key is found.
340     *
341     * @throws ConversionException is thrown if the key maps to an
342     *         object that is not a list of shorts.
343     */
344    public List<Short> getShortList(final String key)
345    {
346        return getShortList(key, new ArrayList<Short>());
347    }
348
349    /**
350     * Get a list of Short objects associated with the given configuration key.
351     * If the key doesn't map to an existing object, the default value is
352     * returned.
353     *
354     * @param key The configuration key.
355     * @param defaultValue The default value.
356     * @return The associated List of Shorts.
357     *
358     * @throws ConversionException is thrown if the key maps to an
359     *         object that is not a list of shorts.
360     */
361    public List<Short> getShortList(final String key, final List<Short> defaultValue)
362    {
363        return getList(Short.class, key, defaultValue);
364    }
365
366    /**
367     * Get an array of short primitives associated with the given
368     * configuration key. If the key doesn't map to an existing object
369     * an empty array is returned.
370     *
371     * @param key The configuration key.
372     * @return The associated short array if the key is found.
373     *
374     * @throws ConversionException is thrown if the key maps to an
375     *         object that is not a list of shorts.
376     */
377    public short[] getShortArray(final String key)
378    {
379        return getShortArray(key, new short[0]);
380    }
381
382    /**
383     * Get an array of short primitives associated with the given
384     * configuration key. If the key doesn't map to an existing object
385     * an empty array is returned.
386     *
387     * @param key The configuration key.
388     * @param defaultValue the default value, which will be returned if the property is not found
389     * @return The associated short array if the key is found.
390     *
391     * @throws ConversionException is thrown if the key maps to an
392     *         object that is not a list of shorts.
393     */
394    public short[] getShortArray(final String key, final short[] defaultValue)
395    {
396        return get(short[].class, key, defaultValue);
397    }
398
399    /**
400     * Get a list of Integer objects associated with the given
401     * configuration key. If the key doesn't map to an existing object
402     * an empty list is returned.
403     *
404     * @param key The configuration key.
405     * @return The associated Integer list if the key is found.
406     *
407     * @throws ConversionException is thrown if the key maps to an
408     *         object that is not a list of integers.
409     */
410    public List<Integer> getIntegerList(final String key)
411    {
412        return getIntegerList(key, new ArrayList<Integer>());
413    }
414
415    /**
416     * Get a list of Integer objects associated with the given
417     * configuration key. If the key doesn't map to an existing object,
418     * the default value is returned.
419     *
420     * @param key The configuration key.
421     * @param defaultValue The default value.
422     * @return The associated List of Integers.
423     *
424     * @throws ConversionException is thrown if the key maps to an
425     *         object that is not a list of integers.
426     */
427    public List<Integer> getIntegerList(final String key, final List<Integer> defaultValue)
428    {
429        return getList(Integer.class, key, defaultValue);
430    }
431
432    /**
433     * Get an array of int primitives associated with the given
434     * configuration key. If the key doesn't map to an existing object
435     * an empty array is returned.
436     *
437     * @param key The configuration key.
438     * @return The associated int array if the key is found.
439     *
440     * @throws ConversionException is thrown if the key maps to an
441     *         object that is not a list of integers.
442     */
443    public int[] getIntArray(final String key)
444    {
445        return getIntArray(key, new int[0]);
446    }
447
448    /**
449     * Get an array of int primitives associated with the given
450     * configuration key. If the key doesn't map to an existing object
451     * an empty array is returned.
452     *
453     * @param key The configuration key.
454     * @param defaultValue the default value, which will be returned if the property is not found
455     * @return The associated int array if the key is found.
456     *
457     * @throws ConversionException is thrown if the key maps to an
458     *         object that is not a list of integers.
459     */
460    public int[] getIntArray(final String key, final int[] defaultValue)
461    {
462        return get(int[].class, key, defaultValue);
463    }
464
465    /**
466     * Get a list of Long objects associated with the given configuration key.
467     * If the key doesn't map to an existing object an empty list is returned.
468     *
469     * @param key The configuration key.
470     * @return The associated Long list if the key is found.
471     *
472     * @throws ConversionException is thrown if the key maps to an
473     *         object that is not a list of longs.
474     */
475    public List<Long> getLongList(final String key)
476    {
477        return getLongList(key, new ArrayList<Long>());
478    }
479
480    /**
481     * Get a list of Long objects associated with the given configuration key.
482     * If the key doesn't map to an existing object, the default value is
483     * returned.
484     *
485     * @param key The configuration key.
486     * @param defaultValue The default value.
487     * @return The associated List of Longs.
488     *
489     * @throws ConversionException is thrown if the key maps to an
490     *         object that is not a list of longs.
491     */
492    public List<Long> getLongList(final String key, final List<Long> defaultValue)
493    {
494        return getList(Long.class, key, defaultValue);
495    }
496
497    /**
498     * Get an array of long primitives associated with the given
499     * configuration key. If the key doesn't map to an existing object
500     * an empty array is returned.
501     *
502     * @param key The configuration key.
503     * @return The associated long array if the key is found.
504     *
505     * @throws ConversionException is thrown if the key maps to an
506     *         object that is not a list of longs.
507     */
508    public long[] getLongArray(final String key)
509    {
510        return getLongArray(key, new long[0]);
511    }
512
513    /**
514     * Get an array of long primitives associated with the given
515     * configuration key. If the key doesn't map to an existing object
516     * an empty array is returned.
517     *
518     * @param key The configuration key.
519     * @param defaultValue the default value, which will be returned if the property is not found
520     * @return The associated long array if the key is found.
521     *
522     * @throws ConversionException is thrown if the key maps to an
523     *         object that is not a list of longs.
524     */
525    public long[] getLongArray(final String key, final long[] defaultValue)
526    {
527        return get(long[].class, key, defaultValue);
528    }
529
530    /**
531     * Get a list of Float objects associated with the given configuration key.
532     * If the key doesn't map to an existing object an empty list is returned.
533     *
534     * @param key The configuration key.
535     * @return The associated Float list if the key is found.
536     *
537     * @throws ConversionException is thrown if the key maps to an
538     *         object that is not a list of floats.
539     */
540    public List<Float> getFloatList(final String key)
541    {
542        return getFloatList(key, new ArrayList<Float>());
543    }
544
545    /**
546     * Get a list of Float objects associated with the given
547     * configuration key. If the key doesn't map to an existing object,
548     * the default value is returned.
549     *
550     * @param key The configuration key.
551     * @param defaultValue The default value.
552     * @return The associated List of Floats.
553     *
554     * @throws ConversionException is thrown if the key maps to an
555     *         object that is not a list of floats.
556     */
557    public List<Float> getFloatList(final String key, final List<Float> defaultValue)
558    {
559        return getList(Float.class, key, defaultValue);
560    }
561
562    /**
563     * Get an array of float primitives associated with the given
564     * configuration key. If the key doesn't map to an existing object
565     * an empty array is returned.
566     *
567     * @param key The configuration key.
568     * @return The associated float array if the key is found.
569     *
570     * @throws ConversionException is thrown if the key maps to an
571     *         object that is not a list of floats.
572     */
573    public float[] getFloatArray(final String key)
574    {
575        return getFloatArray(key, new float[0]);
576    }
577
578    /**
579     * Get an array of float primitives associated with the given
580     * configuration key. If the key doesn't map to an existing object
581     * an empty array is returned.
582     *
583     * @param key The configuration key.
584     * @param defaultValue the default value, which will be returned if the property is not found
585     * @return The associated float array if the key is found.
586     *
587     * @throws ConversionException is thrown if the key maps to an
588     *         object that is not a list of floats.
589     */
590    public float[] getFloatArray(final String key, final float[] defaultValue)
591    {
592        return get(float[].class, key, defaultValue);
593    }
594
595    /**
596     * Get a list of Double objects associated with the given
597     * configuration key. If the key doesn't map to an existing object
598     * an empty list is returned.
599     *
600     * @param key The configuration key.
601     * @return The associated Double list if the key is found.
602     *
603     * @throws ConversionException is thrown if the key maps to an
604     *         object that is not a list of doubles.
605     */
606    public List<Double> getDoubleList(final String key)
607    {
608        return getDoubleList(key, new ArrayList<Double>());
609    }
610
611    /**
612     * Get a list of Double objects associated with the given
613     * configuration key. If the key doesn't map to an existing object,
614     * the default value is returned.
615     *
616     * @param key The configuration key.
617     * @param defaultValue The default value.
618     * @return The associated List of Doubles.
619     *
620     * @throws ConversionException is thrown if the key maps to an
621     *         object that is not a list of doubles.
622     */
623    public List<Double> getDoubleList(final String key, final List<Double> defaultValue)
624    {
625        return getList(Double.class, key, defaultValue);
626    }
627
628    /**
629     * Get an array of double primitives associated with the given
630     * configuration key. If the key doesn't map to an existing object
631     * an empty array is returned.
632     *
633     * @param key The configuration key.
634     * @return The associated double array if the key is found.
635     *
636     * @throws ConversionException is thrown if the key maps to an
637     *         object that is not a list of doubles.
638     */
639    public double[] getDoubleArray(final String key)
640    {
641        return getDoubleArray(key, new double[0]);
642    }
643
644    /**
645     * Get an array of double primitives associated with the given
646     * configuration key. If the key doesn't map to an existing object
647     * an empty array is returned.
648     *
649     * @param key The configuration key.
650     * @param defaultValue the default value, which will be returned if the property is not found
651     * @return The associated double array if the key is found.
652     *
653     * @throws ConversionException is thrown if the key maps to an
654     *         object that is not a list of doubles.
655     */
656    public double[] getDoubleArray(final String key, final double[] defaultValue)
657    {
658        return get(double[].class, key, defaultValue);
659    }
660
661    /**
662     * Get a list of BigIntegers associated with the given configuration key.
663     * If the key doesn't map to an existing object an empty list is returned.
664     *
665     * @param key The configuration key.
666     * @return The associated BigInteger list if the key is found.
667     *
668     * @throws ConversionException is thrown if the key maps to an
669     *         object that is not a list of BigIntegers.
670     */
671    public List<BigInteger> getBigIntegerList(final String key)
672    {
673        return getBigIntegerList(key, new ArrayList<BigInteger>());
674    }
675
676    /**
677     * Get a list of BigIntegers associated with the given configuration key.
678     * If the key doesn't map to an existing object, the default value is
679     * returned.
680     *
681     * @param key The configuration key.
682     * @param defaultValue The default value.
683     * @return The associated List of BigIntegers.
684     *
685     * @throws ConversionException is thrown if the key maps to an
686     *         object that is not a list of BigIntegers.
687     */
688    public List<BigInteger> getBigIntegerList(final String key, final List<BigInteger> defaultValue)
689    {
690        return getList(BigInteger.class, key, defaultValue);
691    }
692
693    /**
694     * Get an array of BigIntegers associated with the given
695     * configuration key. If the key doesn't map to an existing object
696     * an empty array is returned.
697     *
698     * @param key The configuration key.
699     * @return The associated BigInteger array if the key is found.
700     *
701     * @throws ConversionException is thrown if the key maps to an
702     *         object that is not a list of BigIntegers.
703     */
704    public BigInteger[] getBigIntegerArray(final String key)
705    {
706        return getBigIntegerArray(key, new BigInteger[0]);
707    }
708
709    /**
710     * Get an array of BigIntegers associated with the given
711     * configuration key. If the key doesn't map to an existing object
712     * an empty array is returned.
713     *
714     * @param key The configuration key.
715     * @param defaultValue the default value, which will be returned if the property is not found
716     * @return The associated BigInteger array if the key is found.
717     *
718     * @throws ConversionException is thrown if the key maps to an
719     *         object that is not a list of BigIntegers.
720     */
721    public BigInteger[] getBigIntegerArray(final String key, final BigInteger[] defaultValue)
722    {
723        return get(BigInteger[].class, key, defaultValue);
724    }
725
726    /**
727     * Get a list of BigDecimals associated with the given configuration key.
728     * If the key doesn't map to an existing object an empty list is returned.
729     *
730     * @param key The configuration key.
731     * @return The associated BigDecimal list if the key is found.
732     *
733     * @throws ConversionException is thrown if the key maps to an
734     *         object that is not a list of BigDecimals.
735     */
736    public List<BigDecimal> getBigDecimalList(final String key)
737    {
738        return getBigDecimalList(key, new ArrayList<BigDecimal>());
739    }
740
741    /**
742     * Get a list of BigDecimals associated with the given configuration key.
743     * If the key doesn't map to an existing object, the default value is
744     * returned.
745     *
746     * @param key The configuration key.
747     * @param defaultValue The default value.
748     * @return The associated List of BigDecimals.
749     *
750     * @throws ConversionException is thrown if the key maps to an
751     *         object that is not a list of BigDecimals.
752     */
753    public List<BigDecimal> getBigDecimalList(final String key, final List<BigDecimal> defaultValue)
754    {
755        return getList(BigDecimal.class, key, defaultValue);
756    }
757
758    /**
759     * Get an array of BigDecimals associated with the given
760     * configuration key. If the key doesn't map to an existing object
761     * an empty array is returned.
762     *
763     * @param key The configuration key.
764     * @return The associated BigDecimal array if the key is found.
765     *
766     * @throws ConversionException is thrown if the key maps to an
767     *         object that is not a list of BigDecimals.
768     */
769    public BigDecimal[] getBigDecimalArray(final String key)
770    {
771        return getBigDecimalArray(key, new BigDecimal[0]);
772    }
773
774    /**
775     * Get an array of BigDecimals associated with the given
776     * configuration key. If the key doesn't map to an existing object
777     * an empty array is returned.
778     *
779     * @param key The configuration key.
780     * @param defaultValue the default value, which will be returned if the property is not found
781     * @return The associated BigDecimal array if the key is found.
782     *
783     * @throws ConversionException is thrown if the key maps to an
784     *         object that is not a list of BigDecimals.
785     */
786    public BigDecimal[] getBigDecimalArray(final String key, final BigDecimal[] defaultValue)
787    {
788        return get(BigDecimal[].class, key, defaultValue);
789    }
790
791    /**
792     * Get an URI associated with the given configuration key.
793     *
794     * @param key The configuration key.
795     * @return The associated URI.
796     *
797     * @throws ConversionException is thrown if the key maps to an
798     *         object that is not an URI.
799     */
800    public URI getURI(final String key)
801    {
802        return get(URI.class, key);
803    }
804
805    /**
806     * Get an URI associated with the given configuration key.
807     * If the key doesn't map to an existing object, the default value
808     * is returned.
809     *
810     * @param key          The configuration key.
811     * @param defaultValue The default value.
812     * @return The associated URI.
813     *
814     * @throws ConversionException is thrown if the key maps to an
815     *         object that is not an URI.
816     */
817    public URI getURI(final String key, final URI defaultValue)
818    {
819        return get(URI.class, key, defaultValue);
820    }
821
822    /**
823     * Get an array of URIs associated with the given configuration key.
824     * If the key doesn't map to an existing object an empty array is returned.
825     *
826     * @param key The configuration key.
827     * @return The associated URI array if the key is found.
828     *
829     * @throws ConversionException is thrown if the key maps to an
830     *         object that is not a list of URIs.
831     */
832    public URI[] getURIArray(final String key)
833    {
834        return getURIArray(key, new URI[0]);
835    }
836
837    /**
838     * Get an array of URIs associated with the given configuration key.
839     * If the key doesn't map to an existing object an empty array is returned.
840     *
841     * @param key The configuration key.
842     * @param defaultValue the default value, which will be returned if the property is not found
843     * @return The associated URI array if the key is found.
844     *
845     * @throws ConversionException is thrown if the key maps to an
846     *         object that is not a list of URIs.
847     */
848    public URI[] getURIArray(final String key, final URI[] defaultValue)
849    {
850        return get(URI[].class, key, defaultValue);
851    }
852
853    /**
854     * Get a list of URIs associated with the given configuration key.
855     * If the key doesn't map to an existing object an empty list is returned.
856     *
857     * @param key The configuration key.
858     * @return The associated URI list if the key is found.
859     *
860     * @throws ConversionException is thrown if the key maps to an
861     *         object that is not a list of URIs.
862     */
863    public List<URI> getURIList(final String key)
864    {
865        return getURIList(key, new ArrayList<URI>());
866    }
867
868    /**
869     * Get a list of URIs associated with the given configuration key.
870     * If the key doesn't map to an existing object, the default value is
871     * returned.
872     *
873     * @param key The configuration key.
874     * @param defaultValue The default value.
875     * @return The associated List of URIs.
876     *
877     * @throws ConversionException is thrown if the key maps to an
878     *         object that is not a list of URIs.
879     */
880    public List<URI> getURIList(final String key, final List<URI> defaultValue)
881    {
882        return getList(URI.class, key, defaultValue);
883    }
884
885    /**
886     * Get an URL associated with the given configuration key.
887     *
888     * @param key The configuration key.
889     * @return The associated URL.
890     *
891     * @throws ConversionException is thrown if the key maps to an
892     *         object that is not an URL.
893     */
894    public URL getURL(final String key)
895    {
896        return get(URL.class, key);
897    }
898
899    /**
900     * Get an URL associated with the given configuration key.
901     * If the key doesn't map to an existing object, the default value
902     * is returned.
903     *
904     * @param key          The configuration key.
905     * @param defaultValue The default value.
906     * @return The associated URL.
907     *
908     * @throws ConversionException is thrown if the key maps to an
909     *         object that is not an URL.
910     */
911    public URL getURL(final String key, final URL defaultValue)
912    {
913        return get(URL.class, key, defaultValue);
914    }
915
916    /**
917     * Get a list of URLs associated with the given configuration key.
918     * If the key doesn't map to an existing object an empty list is returned.
919     *
920     * @param key The configuration key.
921     * @return The associated URL list if the key is found.
922     *
923     * @throws ConversionException is thrown if the key maps to an
924     *         object that is not a list of URLs.
925     */
926    public List<URL> getURLList(final String key)
927    {
928        return getURLList(key, new ArrayList<URL>());
929    }
930
931    /**
932     * Get a list of URLs associated with the given configuration key.
933     * If the key doesn't map to an existing object, the default value is
934     * returned.
935     *
936     * @param key The configuration key.
937     * @param defaultValue The default value.
938     * @return The associated List of URLs.
939     *
940     * @throws ConversionException is thrown if the key maps to an
941     *         object that is not a list of URLs.
942     */
943    public List<URL> getURLList(final String key, final List<URL> defaultValue)
944    {
945        return getList(URL.class, key, defaultValue);
946    }
947
948    /**
949     * Get an array of URLs associated with the given configuration key.
950     * If the key doesn't map to an existing object an empty array is returned.
951     *
952     * @param key The configuration key.
953     * @return The associated URL array if the key is found.
954     *
955     * @throws ConversionException is thrown if the key maps to an
956     *         object that is not a list of URLs.
957     */
958    public URL[] getURLArray(final String key)
959    {
960        return getURLArray(key, new URL[0]);
961    }
962
963    /**
964     * Get an array of URLs associated with the given configuration key.
965     * If the key doesn't map to an existing object an empty array is returned.
966     *
967     * @param key The configuration key.
968     * @param defaultValue the default value, which will be returned if the property is not found
969     * @return The associated URL array if the key is found.
970     *
971     * @throws ConversionException is thrown if the key maps to an
972     *         object that is not a list of URLs.
973     */
974    public URL[] getURLArray(final String key, final URL[] defaultValue)
975    {
976        return get(URL[].class, key, defaultValue);
977    }
978
979    /**
980     * Get a Date associated with the given configuration key. If the property
981     * is a String, it will be parsed with the format defined by the user in
982     * the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
983     * {@link #DEFAULT_DATE_FORMAT} pattern.
984     *
985     * @param key The configuration key.
986     * @return The associated Date.
987     *
988     * @throws ConversionException is thrown if the key maps to an
989     *         object that is not a Date.
990     */
991    public Date getDate(final String key)
992    {
993        return get(Date.class, key);
994    }
995
996    /**
997     * Get a Date associated with the given configuration key. If the property
998     * is a String, it will be parsed with the specified format pattern.
999     *
1000     * @param key    The configuration key.
1001     * @param format The non-localized {@link java.text.DateFormat} pattern.
1002     * @return The associated Date
1003     *
1004     * @throws ConversionException is thrown if the key maps to an
1005     *         object that is not a Date.
1006     */
1007    public Date getDate(final String key, final String format)
1008    {
1009        final Date value = getDate(key, null, format);
1010        if (value != null)
1011        {
1012            return value;
1013        }
1014        else if (isThrowExceptionOnMissing())
1015        {
1016            throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
1017        }
1018        else
1019        {
1020            return null;
1021        }
1022    }
1023
1024    /**
1025     * Get a Date associated with the given configuration key. If the property
1026     * is a String, it will be parsed with the format defined by the user in
1027     * the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
1028     * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an
1029     * existing object, the default value is returned.
1030     *
1031     * @param key          The configuration key.
1032     * @param defaultValue The default value.
1033     * @return The associated Date.
1034     *
1035     * @throws ConversionException is thrown if the key maps to an
1036     *         object that is not a Date.
1037     */
1038    public Date getDate(final String key, final Date defaultValue)
1039    {
1040        return getDate(key, defaultValue, null);
1041    }
1042
1043    /**
1044     * Get a Date associated with the given configuration key. If the property
1045     * is a String, it will be parsed with the specified format pattern.
1046     * If the key doesn't map to an existing object, the default value
1047     * is returned.
1048     *
1049     * @param key          The configuration key.
1050     * @param defaultValue The default value.
1051     * @param format       The non-localized {@link java.text.DateFormat} pattern.
1052     * @return The associated Date.
1053     *
1054     * @throws ConversionException is thrown if the key maps to an
1055     *         object that is not a Date.
1056     */
1057    public Date getDate(final String key, final Date defaultValue, final String format)
1058    {
1059        TEMP_DATE_FORMAT.set(format);
1060        try
1061        {
1062            return get(Date.class, key, defaultValue);
1063        }
1064        finally
1065        {
1066            TEMP_DATE_FORMAT.remove();
1067        }
1068    }
1069
1070    public List<Date> getDateList(final String key)
1071    {
1072        return getDateList(key, new ArrayList<Date>());
1073    }
1074
1075    /**
1076     * Get a list of Dates associated with the given configuration key.
1077     * If the property is a list of Strings, they will be parsed with the
1078     * specified format pattern. If the key doesn't map to an existing object
1079     * an empty list is returned.
1080     *
1081     * @param key    The configuration key.
1082     * @param format The non-localized {@link java.text.DateFormat} pattern.
1083     * @return The associated Date list if the key is found.
1084     *
1085     * @throws ConversionException is thrown if the key maps to an
1086     *         object that is not a list of Dates.
1087     */
1088    public List<Date> getDateList(final String key, final String format)
1089    {
1090        return getDateList(key, new ArrayList<Date>(), format);
1091    }
1092
1093    /**
1094     * Get a list of Dates associated with the given configuration key.
1095     * If the property is a list of Strings, they will be parsed with the
1096     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1097     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1098     * If the key doesn't map to an existing object, the default value is
1099     * returned.
1100     *
1101     * @param key          The configuration key.
1102     * @param defaultValue The default value.
1103     * @return The associated Date list if the key is found.
1104     *
1105     * @throws ConversionException is thrown if the key maps to an
1106     *         object that is not a list of Dates.
1107     */
1108    public List<Date> getDateList(final String key, final List<Date> defaultValue)
1109    {
1110        return getDateList(key, defaultValue, null);
1111    }
1112
1113    /**
1114     * Get a list of Dates associated with the given configuration key.
1115     * If the property is a list of Strings, they will be parsed with the
1116     * specified format pattern. If the key doesn't map to an existing object,
1117     * the default value is returned.
1118     *
1119     * @param key          The configuration key.
1120     * @param defaultValue The default value.
1121     * @param format       The non-localized {@link java.text.DateFormat} pattern.
1122     * @return The associated Date list if the key is found.
1123     *
1124     * @throws ConversionException is thrown if the key maps to an
1125     *         object that is not a list of Dates.
1126     */
1127    public List<Date> getDateList(final String key, final List<Date> defaultValue, final String format)
1128    {
1129        TEMP_DATE_FORMAT.set(format);
1130        try
1131        {
1132            return getList(Date.class, key, defaultValue);
1133        }
1134        finally
1135        {
1136            TEMP_DATE_FORMAT.remove();
1137        }
1138    }
1139
1140    /**
1141     * Get an array of Dates associated with the given configuration key.
1142     * If the property is a list of Strings, they will be parsed with the
1143     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1144     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1145     * If the key doesn't map to an existing object an empty array is returned.
1146     *
1147     * @param key The configuration key.
1148     * @return The associated Date array if the key is found.
1149     *
1150     * @throws ConversionException is thrown if the key maps to an
1151     *         object that is not a list of Dates.
1152     */
1153    public Date[] getDateArray(final String key)
1154    {
1155        return getDateArray(key, new Date[0]);
1156    }
1157
1158    /**
1159     * Get an array of Dates associated with the given configuration key.
1160     * If the property is a list of Strings, they will be parsed with the
1161     * specified format pattern. If the key doesn't map to an existing object
1162     * an empty array is returned.
1163     *
1164     * @param key    The configuration key.
1165     * @param format The non-localized {@link java.text.DateFormat} pattern.
1166     * @return The associated Date array if the key is found.
1167     *
1168     * @throws ConversionException is thrown if the key maps to an
1169     *         object that is not a list of Dates.
1170     */
1171    public Date[] getDateArray(final String key, final String format)
1172    {
1173        return getDateArray(key, new Date[0], format);
1174    }
1175
1176    /**
1177     * Get an array of Dates associated with the given configuration key.
1178     * If the property is a list of Strings, they will be parsed with the
1179     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1180     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1181     * If the key doesn't map to an existing 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 Date array if the key is found.
1186     *
1187     * @throws ConversionException is thrown if the key maps to an
1188     *         object that is not a list of Dates.
1189     */
1190    public Date[] getDateArray(final String key, final Date[] defaultValue)
1191    {
1192        return getDateArray(key, defaultValue, null);
1193    }
1194
1195    /**
1196     * Get an array of Dates associated with the given configuration key.
1197     * If the property is a list of Strings, they will be parsed with the
1198     * specified format pattern. If the key doesn't map to an existing object,
1199     * the default value is returned.
1200     *
1201     * @param key          The configuration key.
1202     * @param defaultValue The default value.
1203     * @param format       The non-localized {@link java.text.DateFormat} pattern.
1204     * @return The associated Date array if the key is found.
1205     *
1206     * @throws ConversionException is thrown if the key maps to an
1207     *         object that is not a list of Dates.
1208     */
1209    public Date[] getDateArray(final String key, final Date[] defaultValue, final String format)
1210    {
1211        TEMP_DATE_FORMAT.set(format);
1212        try
1213        {
1214            return get(Date[].class, key, defaultValue);
1215        }
1216        finally
1217        {
1218            TEMP_DATE_FORMAT.remove();
1219        }
1220    }
1221
1222    /**
1223     * Get a Calendar associated with the given configuration key. If the
1224     * property is a String, it will be parsed with the format defined by the
1225     * user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
1226     * with the {@link #DEFAULT_DATE_FORMAT} pattern.
1227     *
1228     * @param key The configuration key.
1229     * @return The associated Calendar.
1230     *
1231     * @throws ConversionException is thrown if the key maps to an
1232     *         object that is not a Calendar.
1233     */
1234    public Calendar getCalendar(final String key)
1235    {
1236        return get(Calendar.class, key);
1237    }
1238
1239    /**
1240     * Get a Calendar associated with the given configuration key. If the
1241     * property is a String, it will be parsed with the specified format
1242     * pattern.
1243     *
1244     * @param key    The configuration key.
1245     * @param format The non-localized {@link java.text.DateFormat} pattern.
1246     * @return The associated Calendar
1247     *
1248     * @throws ConversionException is thrown if the key maps to an
1249     *         object that is not a Calendar.
1250     */
1251    public Calendar getCalendar(final String key, final String format)
1252    {
1253        final Calendar value = getCalendar(key, null, format);
1254        if (value != null)
1255        {
1256            return value;
1257        }
1258        else if (isThrowExceptionOnMissing())
1259        {
1260            throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
1261        }
1262        else
1263        {
1264            return null;
1265        }
1266    }
1267
1268    /**
1269     * Get a Calendar associated with the given configuration key. If the
1270     * property is a String, it will be parsed with the format defined by the
1271     * user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
1272     * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map
1273     * to an existing object, the default value is returned.
1274     *
1275     * @param key          The configuration key.
1276     * @param defaultValue The default value.
1277     * @return The associated Calendar.
1278     *
1279     * @throws ConversionException is thrown if the key maps to an
1280     *         object that is not a Calendar.
1281     */
1282    public Calendar getCalendar(final String key, final Calendar defaultValue)
1283    {
1284        return getCalendar(key, defaultValue, null);
1285    }
1286
1287    /**
1288     * Get a Calendar associated with the given configuration key. If the
1289     * property is a String, it will be parsed with the specified format
1290     * pattern. If the key doesn't map to an existing object, the default
1291     * value is returned.
1292     *
1293     * @param key          The configuration key.
1294     * @param defaultValue The default value.
1295     * @param format       The non-localized {@link java.text.DateFormat} pattern.
1296     * @return The associated Calendar.
1297     *
1298     * @throws ConversionException is thrown if the key maps to an
1299     *         object that is not a Calendar.
1300     */
1301    public Calendar getCalendar(final String key, final Calendar defaultValue, final String format)
1302    {
1303        TEMP_DATE_FORMAT.set(format);
1304        try
1305        {
1306            return get(Calendar.class, key, defaultValue);
1307        }
1308        finally
1309        {
1310            TEMP_DATE_FORMAT.remove();
1311        }
1312    }
1313
1314    /**
1315     * Get a list of Calendars associated with the given configuration key.
1316     * If the property is a list of Strings, they will be parsed with the
1317     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1318     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1319     * If the key doesn't map to an existing object an empty list is returned.
1320     *
1321     * @param key The configuration key.
1322     * @return The associated Calendar list if the key is found.
1323     *
1324     * @throws ConversionException is thrown if the key maps to an
1325     *         object that is not a list of Calendars.
1326     */
1327    public List<Calendar> getCalendarList(final String key)
1328    {
1329        return getCalendarList(key, new ArrayList<Calendar>());
1330    }
1331
1332    /**
1333     * Get a list of Calendars associated with the given configuration key.
1334     * If the property is a list of Strings, they will be parsed with the
1335     * specified format pattern. If the key doesn't map to an existing object
1336     * an empty list is returned.
1337     *
1338     * @param key    The configuration key.
1339     * @param format The non-localized {@link java.text.DateFormat} pattern.
1340     * @return The associated Calendar list if the key is found.
1341     *
1342     * @throws ConversionException is thrown if the key maps to an
1343     *         object that is not a list of Calendars.
1344     */
1345    public List<Calendar> getCalendarList(final String key, final String format)
1346    {
1347        return getCalendarList(key, new ArrayList<Calendar>(), format);
1348    }
1349
1350    /**
1351     * Get a list of Calendars associated with the given configuration key.
1352     * If the property is a list of Strings, they will be parsed with the
1353     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1354     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1355     * If the key doesn't map to an existing object, the default value is
1356     * returned.
1357     *
1358     * @param key The configuration key.
1359     * @param defaultValue The default value.
1360     * @return The associated Calendar list if the key is found.
1361     *
1362     * @throws ConversionException is thrown if the key maps to an
1363     *         object that is not a list of Calendars.
1364     */
1365    public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue)
1366    {
1367        return getCalendarList(key, defaultValue, null);
1368    }
1369
1370    /**
1371     * Get a list of Calendars associated with the given configuration key.
1372     * If the property is a list of Strings, they will be parsed with the
1373     * specified format pattern. If the key doesn't map to an existing object,
1374     * the default value is returned.
1375     *
1376     * @param key          The configuration key.
1377     * @param defaultValue The default value.
1378     * @param format       The non-localized {@link java.text.DateFormat} pattern.
1379     * @return The associated Calendar list if the key is found.
1380     *
1381     * @throws ConversionException is thrown if the key maps to an
1382     *         object that is not a list of Calendars.
1383     */
1384    public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue, final String format)
1385    {
1386        TEMP_DATE_FORMAT.set(format);
1387        try
1388        {
1389            return getList(Calendar.class, key, defaultValue);
1390        }
1391        finally
1392        {
1393            TEMP_DATE_FORMAT.remove();
1394        }
1395    }
1396
1397    /**
1398     * Get an array of Calendars associated with the given configuration key.
1399     * If the property is a list of Strings, they will be parsed with the
1400     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1401     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1402     * If the key doesn't map to an existing object an empty array is returned.
1403     *
1404     * @param key The configuration key.
1405     * @return The associated Calendar array if the key is found.
1406     *
1407     * @throws ConversionException is thrown if the key maps to an
1408     *         object that is not a list of Calendars.
1409     */
1410    public Calendar[] getCalendarArray(final String key)
1411    {
1412        return getCalendarArray(key, new Calendar[0]);
1413    }
1414
1415    /**
1416     * Get an array of Calendars associated with the given configuration key.
1417     * If the property is a list of Strings, they will be parsed with the
1418     * specified format pattern. If the key doesn't map to an existing object
1419     * an empty array is returned.
1420     *
1421     * @param key    The configuration key.
1422     * @param format The non-localized {@link java.text.DateFormat} pattern.
1423     * @return The associated Calendar array if the key is found.
1424     *
1425     * @throws ConversionException is thrown if the key maps to an
1426     *         object that is not a list of Calendars.
1427     */
1428    public Calendar[] getCalendarArray(final String key, final String format)
1429    {
1430        return getCalendarArray(key, new Calendar[0], format);
1431    }
1432
1433    /**
1434     * Get an array of Calendars associated with the given configuration key.
1435     * If the property is a list of Strings, they will be parsed with the
1436     * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1437     * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1438     * If the key doesn't map to an existing object an empty array is returned.
1439     *
1440     * @param key The configuration key.
1441     * @param defaultValue the default value, which will be returned if the property is not found
1442     * @return The associated Calendar array if the key is found.
1443     *
1444     * @throws ConversionException is thrown if the key maps to an
1445     *         object that is not a list of Calendars.
1446     */
1447    public Calendar[] getCalendarArray(final String key, final Calendar[] defaultValue)
1448    {
1449        return getCalendarArray(key, defaultValue, null);
1450    }
1451
1452    /**
1453     * Get an array of Calendars associated with the given configuration key.
1454     * If the property is a list of Strings, they will be parsed with the
1455     * specified format pattern. If the key doesn't map to an existing object,
1456     * the default value is returned.
1457     *
1458     * @param key          The configuration key.
1459     * @param defaultValue The default value.
1460     * @param format       The non-localized {@link java.text.DateFormat} pattern.
1461     * @return The associated Calendar array if the key is found.
1462     *
1463     * @throws ConversionException is thrown if the key maps to an
1464     *         object that is not a list of Calendars.
1465     */
1466    public Calendar[] getCalendarArray(final String key, final Calendar[] defaultValue, final String format)
1467    {
1468        TEMP_DATE_FORMAT.set(format);
1469        try
1470        {
1471            return get(Calendar[].class, key, defaultValue);
1472        }
1473        finally
1474        {
1475            TEMP_DATE_FORMAT.remove();
1476        }
1477    }
1478
1479    /**
1480     * Returns the date format specified by the user in the DATE_FORMAT_KEY
1481     * property, or the default format otherwise.
1482     *
1483     * @return the default date format
1484     */
1485    private String getDefaultDateFormat()
1486    {
1487        return getString(DATE_FORMAT_KEY, DEFAULT_DATE_FORMAT);
1488    }
1489
1490    /**
1491     * Get a Locale associated with the given configuration key.
1492     *
1493     * @param key The configuration key.
1494     * @return The associated Locale.
1495     *
1496     * @throws ConversionException is thrown if the key maps to an
1497     *         object that is not a Locale.
1498     */
1499    public Locale getLocale(final String key)
1500    {
1501        return get(Locale.class, key);
1502    }
1503
1504    /**
1505     * Get a Locale associated with the given configuration key.
1506     * If the key doesn't map to an existing object, the default value
1507     * is returned.
1508     *
1509     * @param key          The configuration key.
1510     * @param defaultValue The default value.
1511     * @return The associated Locale.
1512     *
1513     * @throws ConversionException is thrown if the key maps to an
1514     *         object that is not a Locale.
1515     */
1516    public Locale getLocale(final String key, final Locale defaultValue)
1517    {
1518        return get(Locale.class, key, defaultValue);
1519    }
1520
1521    /**
1522     * Get a list of Locales associated with the given configuration key.
1523     * If the key doesn't map to an existing object an empty list is returned.
1524     *
1525     * @param key The configuration key.
1526     * @return The associated Locale list if the key is found.
1527     *
1528     * @throws ConversionException is thrown if the key maps to an
1529     *         object that is not a list of Locales.
1530     */
1531    public List<Locale> getLocaleList(final String key)
1532    {
1533        return getLocaleList(key, new ArrayList<Locale>());
1534    }
1535
1536    /**
1537     * Get a list of Locales associated with the given configuration key.
1538     * If the key doesn't map to an existing object, the default value is
1539     * returned.
1540     *
1541     * @param key The configuration key.
1542     * @param defaultValue The default value.
1543     * @return The associated List of Locales.
1544     *
1545     * @throws ConversionException is thrown if the key maps to an
1546     *         object that is not a list of Locales.
1547     */
1548    public List<Locale> getLocaleList(final String key, final List<Locale> defaultValue)
1549    {
1550        return getList(Locale.class, key, defaultValue);
1551    }
1552
1553    /**
1554     * Get an array of Locales associated with the given
1555     * configuration key. If the key doesn't map to an existing object
1556     * an empty array is returned.
1557     *
1558     * @param key The configuration key.
1559     * @return The associated Locale array if the key is found.
1560     *
1561     * @throws ConversionException is thrown if the key maps to an
1562     *         object that is not a list of Locales.
1563     */
1564    public Locale[] getLocaleArray(final String key)
1565    {
1566        return getLocaleArray(key, new Locale[0]);
1567    }
1568
1569    /**
1570     * Get an array of Locales associated with the given
1571     * configuration key. If the key doesn't map to an existing object
1572     * an empty array is returned.
1573     *
1574     * @param key The configuration key.
1575     * @param defaultValue the default value, which will be returned if the property is not found
1576     * @return The associated Locale array if the key is found.
1577     *
1578     * @throws ConversionException is thrown if the key maps to an
1579     *         object that is not a list of Locales.
1580     */
1581    public Locale[] getLocaleArray(final String key, final Locale[] defaultValue)
1582    {
1583        return get(Locale[].class, key, defaultValue);
1584    }
1585
1586    /**
1587     * Get a Color associated with the given configuration key.
1588     *
1589     * @param key The configuration key.
1590     * @return The associated Color.
1591     *
1592     * @throws ConversionException is thrown if the key maps to an
1593     *         object that is not a Color.
1594     */
1595    public Color getColor(final String key)
1596    {
1597        return get(Color.class, key);
1598    }
1599
1600    /**
1601     * Get a Color associated with the given configuration key.
1602     * If the key doesn't map to an existing object, the default value
1603     * is returned.
1604     *
1605     * @param key          The configuration key.
1606     * @param defaultValue The default value.
1607     * @return The associated Color.
1608     *
1609     * @throws ConversionException is thrown if the key maps to an
1610     *         object that is not a Color.
1611     */
1612    public Color getColor(final String key, final Color defaultValue)
1613    {
1614        return get(Color.class, key, defaultValue);
1615    }
1616
1617    /**
1618     * Get a list of Colors associated with the given configuration key.
1619     * If the key doesn't map to an existing object an empty list is returned.
1620     *
1621     * @param key The configuration key.
1622     * @return The associated Color list if the key is found.
1623     *
1624     * @throws ConversionException is thrown if the key maps to an
1625     *         object that is not a list of Colors.
1626     */
1627    public List<Color> getColorList(final String key)
1628    {
1629        return getColorList(key, new ArrayList<Color>());
1630    }
1631
1632    /**
1633     * Get a list of Colors associated with the given configuration key.
1634     * If the key doesn't map to an existing object, the default value is
1635     * returned.
1636     *
1637     * @param key The configuration key.
1638     * @param defaultValue The default value.
1639     * @return The associated List of Colors.
1640     *
1641     * @throws ConversionException is thrown if the key maps to an
1642     *         object that is not a list of Colors.
1643     */
1644    public List<Color> getColorList(final String key, final List<Color> defaultValue)
1645    {
1646        return getList(Color.class, key, defaultValue);
1647    }
1648
1649    /**
1650     * Get an array of Colors associated with the given
1651     * configuration key. If the key doesn't map to an existing object
1652     * an empty array is returned.
1653     *
1654     * @param key The configuration key.
1655     * @return The associated Color array if the key is found.
1656     *
1657     * @throws ConversionException is thrown if the key maps to an
1658     *         object that is not a list of Colors.
1659     */
1660    public Color[] getColorArray(final String key)
1661    {
1662        return getColorArray(key, new Color[0]);
1663    }
1664
1665    /**
1666     * Get an array of Colors associated with the given
1667     * configuration key. If the key doesn't map to an existing object
1668     * an empty array is returned.
1669     *
1670     * @param key The configuration key.
1671     * @param defaultValue the default value, which will be returned if the property is not found
1672     * @return The associated Color array if the key is found.
1673     *
1674     * @throws ConversionException is thrown if the key maps to an
1675     *         object that is not a list of Colors.
1676     */
1677    public Color[] getColorArray(final String key, final Color[] defaultValue)
1678    {
1679        return get(Color[].class, key, defaultValue);
1680    }
1681
1682    /**
1683     * Returns the original conversion handler set for this configuration. If
1684     * this is not a {@code DefaultConversionHandler}, result is <b>null</b>.
1685     *
1686     * @return the original conversion handler or <b>null</b>
1687     */
1688    private DefaultConversionHandler getOriginalConversionHandler()
1689    {
1690        final ConversionHandler handler = super.getConversionHandler();
1691        return (DefaultConversionHandler) (handler instanceof DefaultConversionHandler ? handler
1692                : null);
1693    }
1694
1695    /**
1696     * A specialized {@code ConversionHandler} implementation which allows
1697     * overriding the date format pattern. This class takes care that the format
1698     * pattern can be defined as a property of the wrapped configuration or
1699     * temporarily passed when calling a conversion method.
1700     */
1701    private class DataConversionHandler extends DefaultConversionHandler
1702    {
1703        /**
1704         * {@inheritDoc} This implementation checks for a defined data format in
1705         * the following order:
1706         * <ul>
1707         * <li>If a temporary date format is set for the current call, it is
1708         * used.</li>
1709         * <li>If a date format is specified in this configuration using the
1710         * {@code DATE_FORMAT_KEY} property, it is used.</li>
1711         * <li>Otherwise, the date format set for the original conversion
1712         * handler is used if available.</li>
1713         * </ul>
1714         */
1715        @Override
1716        public String getDateFormat()
1717        {
1718            if (StringUtils.isNotEmpty(TEMP_DATE_FORMAT.get()))
1719            {
1720                return TEMP_DATE_FORMAT.get();
1721            }
1722            if (containsKey(DATE_FORMAT_KEY))
1723            {
1724                return getDefaultDateFormat();
1725            }
1726
1727            final DefaultConversionHandler orgHandler =
1728                    getOriginalConversionHandler();
1729            return orgHandler != null ? orgHandler.getDateFormat() : null;
1730        }
1731    }
1732}