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