View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration;
19  
20  import java.awt.Color;
21  import java.io.Serializable;
22  import java.lang.reflect.Array;
23  import java.math.BigDecimal;
24  import java.math.BigInteger;
25  import java.net.URL;
26  import java.util.ArrayList;
27  import java.util.Calendar;
28  import java.util.Collection;
29  import java.util.Date;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.NoSuchElementException;
34  
35  import org.apache.commons.lang.ClassUtils;
36  import org.apache.commons.lang.StringUtils;
37  
38  /**
39   * Decorator providing additional getters for any Configuration. This extended
40   * Configuration supports more types:
41   * <ul>
42   *   <li>{@link java.net.URL}</li>
43   *   <li>{@link java.util.Locale}</li>
44   *   <li>{@link java.util.Date}</li>
45   *   <li>{@link java.util.Calendar}</li>
46   *   <li>{@link java.awt.Color}</li>
47   *   <li>{@link java.net.InetAddress}</li>
48   *   <li>{@link javax.mail.internet.InternetAddress} (requires Javamail in the classpath)</li>
49   *   <li>{@link java.lang.Enum} (Java 5 enumeration types)</li>
50   * </ul>
51   *
52   * Lists and arrays are available for all types.
53   *
54   * <h4>Example</h4>
55   *
56   * Configuration file <tt>config.properties</tt>:
57   * <pre>
58   * title.color = #0000FF
59   * remote.host = 192.168.0.53
60   * default.locales = fr,en,de
61   * email.contact = ebourg@apache.org, oheger@apache.org
62   * </pre>
63   *
64   * Usage:
65   *
66   * <pre>
67   * DataConfiguration config = new DataConfiguration(new PropertiesConfiguration("config.properties"));
68   *
69   * // retrieve a property using a specialized getter
70   * Color color = config.getColor("title.color");
71   *
72   * // retrieve a property using a generic getter
73   * InetAddress host = (InetAddress) config.get(InetAddress.class, "remote.host");
74   * Locale[] locales = (Locale[]) config.getArray(Locale.class, "default.locales");
75   * List contacts = config.getList(InternetAddress.class, "email.contact");
76   * </pre>
77   *
78   * <h4>Dates</h4>
79   *
80   * Date objects are expected to be formatted with the pattern <tt>yyyy-MM-dd HH:mm:ss</tt>.
81   * This default format can be changed by specifying another format in the
82   * getters, or by putting a date format in the configuration under the key
83   * <tt>org.apache.commons.configuration.format.date</tt>.
84   *
85   * @author <a href="ebourg@apache.org">Emmanuel Bourg</a>
86   * @version $Id: DataConfiguration.java 1234985 2012-01-23 21:09:09Z oheger $
87   * @since 1.1
88   */
89  public class DataConfiguration extends AbstractConfiguration implements Serializable
90  {
91      /** The key of the property storing the user defined date format. */
92      public static final String DATE_FORMAT_KEY = "org.apache.commons.configuration.format.date";
93  
94      /** The default format for dates. */
95      public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
96  
97      /**
98       * The serial version UID.
99       */
100     private static final long serialVersionUID = -69011336405718640L;
101 
102     /** Stores the wrapped configuration.*/
103     protected Configuration configuration;
104 
105     /**
106      * Creates a new instance of {@code DataConfiguration} and sets the
107      * wrapped configuration.
108      *
109      * @param configuration the wrapped configuration
110      */
111     public DataConfiguration(Configuration configuration)
112     {
113         this.configuration = configuration;
114     }
115 
116     /**
117      * Return the configuration decorated by this DataConfiguration.
118      *
119      * @return the wrapped configuration
120      */
121     public Configuration getConfiguration()
122     {
123         return configuration;
124     }
125 
126     public Object getProperty(String key)
127     {
128         return configuration.getProperty(key);
129     }
130 
131     @Override
132     protected void addPropertyDirect(String key, Object obj)
133     {
134         if (configuration instanceof AbstractConfiguration)
135         {
136             ((AbstractConfiguration) configuration).addPropertyDirect(key, obj);
137         }
138         else
139         {
140             configuration.addProperty(key, obj);
141         }
142     }
143 
144     @Override
145     public void addProperty(String key, Object value)
146     {
147         getConfiguration().addProperty(key, value);
148     }
149 
150     public boolean isEmpty()
151     {
152         return configuration.isEmpty();
153     }
154 
155     public boolean containsKey(String key)
156     {
157         return configuration.containsKey(key);
158     }
159 
160     @Override
161     public void clearProperty(String key)
162     {
163         configuration.clearProperty(key);
164     }
165 
166     @Override
167     public void setProperty(String key, Object value)
168     {
169         configuration.setProperty(key, value);
170     }
171 
172     public Iterator<String> getKeys()
173     {
174         return configuration.getKeys();
175     }
176 
177     /**
178      * Get an object of the specified type associated with the given
179      * configuration key. If the key doesn't map to an existing object, the
180      * method returns null unless {@link #isThrowExceptionOnMissing()} is set
181      * to <tt>true</tt>.
182      *
183      * @param <T> the target type of the value
184      * @param cls the target class of the value
185      * @param key the key of the value
186      *
187      * @return the value of the requested type for the key
188      *
189      * @throws NoSuchElementException if the key doesn't map to an existing
190      *     object and <tt>throwExceptionOnMissing=true</tt>
191      * @throws ConversionException if the value is not compatible with the requested type
192      *
193      * @since 1.5
194      */
195     public <T> T get(Class<T> cls, String key)
196     {
197         T value = get(cls, key, null);
198         if (value != null)
199         {
200             return value;
201         }
202         else if (isThrowExceptionOnMissing())
203         {
204             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
205         }
206         else
207         {
208             return null;
209         }
210     }
211 
212     /**
213      * Get an object of the specified type associated with the given
214      * configuration key. If the key doesn't map to an existing object, the
215      * default value is returned.
216      *
217      * @param <T>          the target type of the value
218      * @param cls          the target class of the value
219      * @param key          the key of the value
220      * @param defaultValue the default value
221      *
222      * @return the value of the requested type for the key
223      *
224      * @throws ConversionException if the value is not compatible with the requested type
225      *
226      * @since 1.5
227      */
228     public <T> T get(Class<T> cls, String key, T defaultValue)
229     {
230         Object value = resolveContainerStore(key);
231 
232         if (value == null)
233         {
234             return defaultValue;
235         }
236 
237         if (Date.class.equals(cls) || Calendar.class.equals(cls))
238         {
239             return convert(cls, key, interpolate(value), new String[] {getDefaultDateFormat()});
240         }
241         else
242         {
243             return convert(cls, key, interpolate(value), null);
244         }
245     }
246 
247     /**
248      * Get a list of typed objects associated with the given configuration key.
249      * If the key doesn't map to an existing object, an empty list is returned.
250      *
251      * @param <T> the type expected for the elements of the list
252      * @param cls the class expected for the elements of the list
253      * @param key The configuration key.
254      * @return The associated list if the key is found.
255      *
256      * @throws ConversionException is thrown if the key maps to an object that
257      *     is not compatible with a list of the specified class.
258      *
259      * @since 1.5
260      */
261     public <T> List<T> getList(Class<T> cls, String key)
262     {
263         return getList(cls, key, new ArrayList<T>());
264     }
265 
266     /**
267      * Get a list of typed objects associated with the given configuration key.
268      * If the key doesn't map to an existing object, the default value is
269      * returned.
270      *
271      * @param <T>          the type expected for the elements of the list
272      * @param cls          the class expected for the elements of the list
273      * @param key          the configuration key.
274      * @param defaultValue the default value.
275      * @return The associated List.
276      *
277      * @throws ConversionException is thrown if the key maps to an object that
278      *     is not compatible with a list of the specified class.
279      *
280      * @since 1.5
281      */
282     public <T> List<T> getList(Class<T> cls, String key, List<T> defaultValue)
283     {
284         Object value = getProperty(key);
285         Class<?> valueClass = value != null ? value.getClass() : null;
286 
287         List<T> list;
288 
289         if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
290         {
291             // the value is null or is an empty string
292             list = defaultValue;
293         }
294         else
295         {
296             list = new ArrayList<T>();
297 
298             Object[] params = null;
299             if (cls.equals(Date.class) || cls.equals(Calendar.class))
300             {
301                 params = new Object[] {getDefaultDateFormat()};
302             }
303 
304             if (valueClass.isArray())
305             {
306                 // get the class of the objects contained in the array
307                 Class<?> arrayType = valueClass.getComponentType();
308                 int length = Array.getLength(value);
309 
310                 if (arrayType.equals(cls)
311                         || (arrayType.isPrimitive() && cls.equals(ClassUtils.primitiveToWrapper(arrayType))))
312                 {
313                     // the value is an array of the specified type, or an array
314                     // of the primitive type derived from the specified type
315                     for (int i = 0; i < length; i++)
316                     {
317                         list.add(cls.cast(Array.get(value, i)));
318                     }
319                 }
320                 else
321                 {
322                     // attempt to convert the elements of the array
323                     for (int i = 0; i < length; i++)
324                     {
325                         list.add(convert(cls, key, interpolate(Array.get(value, i)), params));
326                     }
327                 }
328             }
329             else if (value instanceof Collection)
330             {
331                 Collection<?> values = (Collection<?>) value;
332 
333                 for (Object o : values)
334                 {
335                     list.add(convert(cls, key, interpolate(o), params));
336                 }
337             }
338             else
339             {
340                 // attempt to convert a single value
341                 list.add(convert(cls, key, interpolate(value), params));
342             }
343         }
344 
345         return list;
346     }
347 
348     /**
349      * Get an array of typed objects associated with the given configuration key.
350      * If the key doesn't map to an existing object, an empty list is returned.
351      *
352      * @param cls the type expected for the elements of the array
353      * @param key The configuration key.
354      * @return The associated array if the key is found, and the value compatible with the type specified.
355      *
356      * @throws ConversionException is thrown if the key maps to an object that
357      *     is not compatible with a list of the specified class.
358      *
359      * @since 1.5
360      */
361     public Object getArray(Class<?> cls, String key)
362     {
363         return getArray(cls, key, Array.newInstance(cls, 0));
364     }
365 
366     /**
367      * Get an array of typed objects associated with the given configuration key.
368      * If the key doesn't map to an existing object, the default value is returned.
369      *
370      * @param cls          the type expected for the elements of the array
371      * @param key          the configuration key.
372      * @param defaultValue the default value
373      * @return The associated array if the key is found, and the value compatible with the type specified.
374      *
375      * @throws ConversionException is thrown if the key maps to an object that
376      *     is not compatible with an array of the specified class.
377      * @throws IllegalArgumentException if the default value is not an array of the specified type
378      *
379      * @since 1.5
380      */
381     public Object getArray(Class<?> cls, String key, Object defaultValue)
382     {
383         // check the type of the default value
384         if (defaultValue != null
385                 && (!defaultValue.getClass().isArray() || !cls
386                         .isAssignableFrom(defaultValue.getClass()
387                                 .getComponentType())))
388         {
389             throw new IllegalArgumentException(
390                     "The type of the default value (" + defaultValue.getClass()
391                             + ")" + " is not an array of the specified class ("
392                             + cls + ")");
393         }
394 
395         if (cls.isPrimitive())
396         {
397             return getPrimitiveArray(cls, key, defaultValue);
398         }
399 
400         List<?> list = getList(cls, key);
401         if (list.isEmpty())
402         {
403             return defaultValue;
404         }
405         else
406         {
407             return list.toArray((Object[]) Array.newInstance(cls, list.size()));
408         }
409     }
410 
411     /**
412      * Get an array of primitive values associated with the given configuration key.
413      * If the key doesn't map to an existing object, the default value is returned.
414      *
415      * @param cls          the primitive type expected for the elements of the array
416      * @param key          the configuration key.
417      * @param defaultValue the default value
418      * @return The associated array if the key is found, and the value compatible with the type specified.
419      *
420      * @throws ConversionException is thrown if the key maps to an object that
421      *     is not compatible with an array of the specified class.
422      *
423      * @since 1.5
424      */
425     private Object getPrimitiveArray(Class<?> cls, String key, Object defaultValue)
426     {
427         Object value = getProperty(key);
428         Class<?> valueClass = value != null ? value.getClass() : null;
429 
430         Object array;
431 
432         if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
433         {
434             // the value is null or is an empty string
435             array = defaultValue;
436         }
437         else
438         {
439             if (valueClass.isArray())
440             {
441                 // get the class of the objects contained in the array
442                 Class<?> arrayType = valueClass.getComponentType();
443                 int length = Array.getLength(value);
444 
445                 if (arrayType.equals(cls))
446                 {
447                     // the value is an array of the same primitive type
448                     array = value;
449                 }
450                 else if (arrayType.equals(ClassUtils.primitiveToWrapper(cls)))
451                 {
452                     // the value is an array of the wrapper type derived from the specified primitive type
453                     array = Array.newInstance(cls, length);
454 
455                     for (int i = 0; i < length; i++)
456                     {
457                         Array.set(array, i, Array.get(value, i));
458                     }
459                 }
460                 else
461                 {
462                     throw new ConversionException('\'' + key + "' (" + arrayType + ")"
463                             + " doesn't map to a compatible array of " + cls);
464                 }
465             }
466             else if (value instanceof Collection)
467             {
468                 Collection<?> values = (Collection<?>) value;
469 
470                 array = Array.newInstance(cls, values.size());
471 
472                 int i = 0;
473                 for (Object o : values)
474                 {
475                     // This is safe because PropertyConverter can handle
476                     // conversion to wrapper classes correctly.
477                     @SuppressWarnings("unchecked")
478                     Object convertedValue = convert(ClassUtils.primitiveToWrapper(cls), key, interpolate(o), null);
479                     Array.set(array, i++, convertedValue);
480                 }
481             }
482             else
483             {
484                 // attempt to convert a single value
485                 // This is safe because PropertyConverter can handle
486                 // conversion to wrapper classes correctly.
487                 @SuppressWarnings("unchecked")
488                 Object convertedValue = convert(ClassUtils.primitiveToWrapper(cls), key, interpolate(value), null);
489 
490                 // create an array of one element
491                 array = Array.newInstance(cls, 1);
492                 Array.set(array, 0, convertedValue);
493             }
494         }
495 
496         return array;
497     }
498 
499     /**
500      * Get a list of Boolean objects associated with the given
501      * configuration key. If the key doesn't map to an existing object
502      * an empty list is returned.
503      *
504      * @param key The configuration key.
505      * @return The associated Boolean list 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 booleans.
509      */
510     public List<Boolean> getBooleanList(String key)
511     {
512         return getBooleanList(key, new ArrayList<Boolean>());
513     }
514 
515     /**
516      * Get a list of Boolean objects associated with the given
517      * configuration key. If the key doesn't map to an existing object,
518      * the default value is returned.
519      *
520      * @param key The configuration key.
521      * @param defaultValue The default value.
522      * @return The associated List of Booleans.
523      *
524      * @throws ConversionException is thrown if the key maps to an
525      *         object that is not a list of booleans.
526      */
527     public List<Boolean> getBooleanList(String key, List<Boolean> defaultValue)
528     {
529          return getList(Boolean.class, key, defaultValue);
530     }
531 
532     /**
533      * Get an array of boolean primitives associated with the given
534      * configuration key. If the key doesn't map to an existing object
535      * an empty array is returned.
536      *
537      * @param key The configuration key.
538      * @return The associated boolean array if the key is found.
539      *
540      * @throws ConversionException is thrown if the key maps to an
541      *         object that is not a list of booleans.
542      */
543     public boolean[] getBooleanArray(String key)
544     {
545         return (boolean[]) getArray(Boolean.TYPE, key);
546     }
547 
548     /**
549      * Get an array of boolean primitives associated with the given
550      * configuration key. If the key doesn't map to an existing object,
551      * the default value is returned.
552      *
553      * @param key          The configuration key.
554      * @param defaultValue The default value.
555      * @return The associated boolean array if the key is found.
556      *
557      * @throws ConversionException is thrown if the key maps to an
558      *         object that is not a list of booleans.
559      */
560     public boolean[] getBooleanArray(String key, boolean[] defaultValue)
561     {
562         return (boolean[]) getArray(Boolean.TYPE, key, defaultValue);
563     }
564 
565     /**
566      * Get a list of Byte objects associated with the given configuration key.
567      * If the key doesn't map to an existing object an empty list is returned.
568      *
569      * @param key The configuration key.
570      * @return The associated Byte list 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 bytes.
574      */
575     public List<Byte> getByteList(String key)
576     {
577         return getByteList(key, new ArrayList<Byte>());
578     }
579 
580     /**
581      * Get a list of Byte objects associated with the given configuration key.
582      * If the key doesn't map to an existing object, the default value is
583      * returned.
584      *
585      * @param key The configuration key.
586      * @param defaultValue The default value.
587      * @return The associated List of Bytes.
588      *
589      * @throws ConversionException is thrown if the key maps to an
590      *         object that is not a list of bytes.
591      */
592     public List<Byte> getByteList(String key, List<Byte> defaultValue)
593     {
594         return getList(Byte.class, key, defaultValue);
595     }
596 
597     /**
598      * Get an array of byte primitives associated with the given
599      * configuration key. If the key doesn't map to an existing object
600      * an empty array is returned.
601      *
602      * @param key The configuration key.
603      * @return The associated byte array 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 bytes.
607      */
608     public byte[] getByteArray(String key)
609     {
610         return getByteArray(key, new byte[0]);
611     }
612 
613     /**
614      * Get an array of byte primitives associated with the given
615      * configuration key. If the key doesn't map to an existing object
616      * an empty array is returned.
617      *
618      * @param key The configuration key.
619      * @param defaultValue the default value, which will be returned if the property is not found
620      * @return The associated byte array if the key is found.
621      *
622      * @throws ConversionException is thrown if the key maps to an
623      *         object that is not a list of bytes.
624      */
625     public byte[] getByteArray(String key, byte[] defaultValue)
626     {
627         return (byte[]) getArray(Byte.TYPE, key, defaultValue);
628     }
629 
630     /**
631      * Get a list of Short objects associated with the given configuration key.
632      * If the key doesn't map to an existing object an empty list is returned.
633      *
634      * @param key The configuration key.
635      * @return The associated Short list if the key is found.
636      *
637      * @throws ConversionException is thrown if the key maps to an
638      *         object that is not a list of shorts.
639      */
640     public List<Short> getShortList(String key)
641     {
642         return getShortList(key, new ArrayList<Short>());
643     }
644 
645     /**
646      * Get a list of Short objects associated with the given configuration key.
647      * If the key doesn't map to an existing object, the default value is
648      * returned.
649      *
650      * @param key The configuration key.
651      * @param defaultValue The default value.
652      * @return The associated List of Shorts.
653      *
654      * @throws ConversionException is thrown if the key maps to an
655      *         object that is not a list of shorts.
656      */
657     public List<Short> getShortList(String key, List<Short> defaultValue)
658     {
659         return getList(Short.class, key, defaultValue);
660     }
661 
662     /**
663      * Get an array of short primitives associated with the given
664      * configuration key. If the key doesn't map to an existing object
665      * an empty array is returned.
666      *
667      * @param key The configuration key.
668      * @return The associated short array 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 shorts.
672      */
673     public short[] getShortArray(String key)
674     {
675         return getShortArray(key, new short[0]);
676     }
677 
678     /**
679      * Get an array of short primitives associated with the given
680      * configuration key. If the key doesn't map to an existing object
681      * an empty array is returned.
682      *
683      * @param key The configuration key.
684      * @param defaultValue the default value, which will be returned if the property is not found
685      * @return The associated short array if the key is found.
686      *
687      * @throws ConversionException is thrown if the key maps to an
688      *         object that is not a list of shorts.
689      */
690     public short[] getShortArray(String key, short[] defaultValue)
691     {
692         return (short[]) getArray(Short.TYPE, key, defaultValue);
693     }
694 
695     /**
696      * Get a list of Integer objects associated with the given
697      * configuration key. If the key doesn't map to an existing object
698      * an empty list is returned.
699      *
700      * @param key The configuration key.
701      * @return The associated Integer list 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 integers.
705      */
706     public List<Integer> getIntegerList(String key)
707     {
708         return getIntegerList(key, new ArrayList<Integer>());
709     }
710 
711     /**
712      * Get a list of Integer objects associated with the given
713      * configuration key. If the key doesn't map to an existing object,
714      * the default value is returned.
715      *
716      * @param key The configuration key.
717      * @param defaultValue The default value.
718      * @return The associated List of Integers.
719      *
720      * @throws ConversionException is thrown if the key maps to an
721      *         object that is not a list of integers.
722      */
723     public List<Integer> getIntegerList(String key, List<Integer> defaultValue)
724     {
725         return getList(Integer.class, key, defaultValue);
726     }
727 
728     /**
729      * Get an array of int primitives associated with the given
730      * configuration key. If the key doesn't map to an existing object
731      * an empty array is returned.
732      *
733      * @param key The configuration key.
734      * @return The associated int array if the key is found.
735      *
736      * @throws ConversionException is thrown if the key maps to an
737      *         object that is not a list of integers.
738      */
739     public int[] getIntArray(String key)
740     {
741         return getIntArray(key, new int[0]);
742     }
743 
744     /**
745      * Get an array of int primitives associated with the given
746      * configuration key. If the key doesn't map to an existing object
747      * an empty array is returned.
748      *
749      * @param key The configuration key.
750      * @param defaultValue the default value, which will be returned if the property is not found
751      * @return The associated int array if the key is found.
752      *
753      * @throws ConversionException is thrown if the key maps to an
754      *         object that is not a list of integers.
755      */
756     public int[] getIntArray(String key, int[] defaultValue)
757     {
758         return (int[]) getArray(Integer.TYPE, key, defaultValue);
759     }
760 
761     /**
762      * Get a list of Long objects associated with the given configuration key.
763      * If the key doesn't map to an existing object an empty list is returned.
764      *
765      * @param key The configuration key.
766      * @return The associated Long list 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 longs.
770      */
771     public List<Long> getLongList(String key)
772     {
773         return getLongList(key, new ArrayList<Long>());
774     }
775 
776     /**
777      * Get a list of Long objects associated with the given configuration key.
778      * If the key doesn't map to an existing object, the default value is
779      * returned.
780      *
781      * @param key The configuration key.
782      * @param defaultValue The default value.
783      * @return The associated List of Longs.
784      *
785      * @throws ConversionException is thrown if the key maps to an
786      *         object that is not a list of longs.
787      */
788     public List<Long> getLongList(String key, List<Long> defaultValue)
789     {
790         return getList(Long.class, key, defaultValue);
791     }
792 
793     /**
794      * Get an array of long primitives associated with the given
795      * configuration key. If the key doesn't map to an existing object
796      * an empty array is returned.
797      *
798      * @param key The configuration key.
799      * @return The associated long array if the key is found.
800      *
801      * @throws ConversionException is thrown if the key maps to an
802      *         object that is not a list of longs.
803      */
804     public long[] getLongArray(String key)
805     {
806         return getLongArray(key, new long[0]);
807     }
808 
809     /**
810      * Get an array of long primitives associated with the given
811      * configuration key. If the key doesn't map to an existing object
812      * an empty array is returned.
813      *
814      * @param key The configuration key.
815      * @param defaultValue the default value, which will be returned if the property is not found
816      * @return The associated long array if the key is found.
817      *
818      * @throws ConversionException is thrown if the key maps to an
819      *         object that is not a list of longs.
820      */
821     public long[] getLongArray(String key, long[] defaultValue)
822     {
823         return (long[]) getArray(Long.TYPE, key, defaultValue);
824     }
825 
826     /**
827      * Get a list of Float objects associated with the given configuration key.
828      * If the key doesn't map to an existing object an empty list is returned.
829      *
830      * @param key The configuration key.
831      * @return The associated Float list if the key is found.
832      *
833      * @throws ConversionException is thrown if the key maps to an
834      *         object that is not a list of floats.
835      */
836     public List<Float> getFloatList(String key)
837     {
838         return getFloatList(key, new ArrayList<Float>());
839     }
840 
841     /**
842      * Get a list of Float objects associated with the given
843      * configuration key. If the key doesn't map to an existing object,
844      * the default value is returned.
845      *
846      * @param key The configuration key.
847      * @param defaultValue The default value.
848      * @return The associated List of Floats.
849      *
850      * @throws ConversionException is thrown if the key maps to an
851      *         object that is not a list of floats.
852      */
853     public List<Float> getFloatList(String key, List<Float> defaultValue)
854     {
855         return getList(Float.class, key, defaultValue);
856     }
857 
858     /**
859      * Get an array of float primitives associated with the given
860      * configuration key. If the key doesn't map to an existing object
861      * an empty array is returned.
862      *
863      * @param key The configuration key.
864      * @return The associated float array if the key is found.
865      *
866      * @throws ConversionException is thrown if the key maps to an
867      *         object that is not a list of floats.
868      */
869     public float[] getFloatArray(String key)
870     {
871         return getFloatArray(key, new float[0]);
872     }
873 
874     /**
875      * Get an array of float primitives associated with the given
876      * configuration key. If the key doesn't map to an existing object
877      * an empty array is returned.
878      *
879      * @param key The configuration key.
880      * @param defaultValue the default value, which will be returned if the property is not found
881      * @return The associated float array if the key is found.
882      *
883      * @throws ConversionException is thrown if the key maps to an
884      *         object that is not a list of floats.
885      */
886     public float[] getFloatArray(String key, float[] defaultValue)
887     {
888         return (float[]) getArray(Float.TYPE, key, defaultValue);
889     }
890 
891     /**
892      * Get a list of Double objects associated with the given
893      * configuration key. If the key doesn't map to an existing object
894      * an empty list is returned.
895      *
896      * @param key The configuration key.
897      * @return The associated Double list if the key is found.
898      *
899      * @throws ConversionException is thrown if the key maps to an
900      *         object that is not a list of doubles.
901      */
902     public List<Double> getDoubleList(String key)
903     {
904         return getDoubleList(key, new ArrayList<Double>());
905     }
906 
907     /**
908      * Get a list of Double objects associated with the given
909      * configuration key. If the key doesn't map to an existing object,
910      * the default value is returned.
911      *
912      * @param key The configuration key.
913      * @param defaultValue The default value.
914      * @return The associated List of Doubles.
915      *
916      * @throws ConversionException is thrown if the key maps to an
917      *         object that is not a list of doubles.
918      */
919     public List<Double> getDoubleList(String key, List<Double> defaultValue)
920     {
921         return getList(Double.class, key, defaultValue);
922     }
923 
924     /**
925      * Get an array of double primitives associated with the given
926      * configuration key. If the key doesn't map to an existing object
927      * an empty array is returned.
928      *
929      * @param key The configuration key.
930      * @return The associated double array if the key is found.
931      *
932      * @throws ConversionException is thrown if the key maps to an
933      *         object that is not a list of doubles.
934      */
935     public double[] getDoubleArray(String key)
936     {
937         return getDoubleArray(key, new double[0]);
938     }
939 
940     /**
941      * Get an array of double primitives associated with the given
942      * configuration key. If the key doesn't map to an existing object
943      * an empty array is returned.
944      *
945      * @param key The configuration key.
946      * @param defaultValue the default value, which will be returned if the property is not found
947      * @return The associated double array if the key is found.
948      *
949      * @throws ConversionException is thrown if the key maps to an
950      *         object that is not a list of doubles.
951      */
952     public double[] getDoubleArray(String key, double[] defaultValue)
953     {
954         return (double[]) getArray(Double.TYPE, key, defaultValue);
955     }
956 
957     /**
958      * Get a list of BigIntegers associated with the given configuration key.
959      * If the key doesn't map to an existing object an empty list is returned.
960      *
961      * @param key The configuration key.
962      * @return The associated BigInteger list if the key is found.
963      *
964      * @throws ConversionException is thrown if the key maps to an
965      *         object that is not a list of BigIntegers.
966      */
967     public List<BigInteger> getBigIntegerList(String key)
968     {
969         return getBigIntegerList(key, new ArrayList<BigInteger>());
970     }
971 
972     /**
973      * Get a list of BigIntegers associated with the given configuration key.
974      * If the key doesn't map to an existing object, the default value is
975      * returned.
976      *
977      * @param key The configuration key.
978      * @param defaultValue The default value.
979      * @return The associated List of BigIntegers.
980      *
981      * @throws ConversionException is thrown if the key maps to an
982      *         object that is not a list of BigIntegers.
983      */
984     public List<BigInteger> getBigIntegerList(String key, List<BigInteger> defaultValue)
985     {
986         return getList(BigInteger.class, key, defaultValue);
987     }
988 
989     /**
990      * Get an array of BigIntegers associated with the given
991      * configuration key. If the key doesn't map to an existing object
992      * an empty array is returned.
993      *
994      * @param key The configuration key.
995      * @return The associated BigInteger array if the key is found.
996      *
997      * @throws ConversionException is thrown if the key maps to an
998      *         object that is not a list of BigIntegers.
999      */
1000     public BigInteger[] getBigIntegerArray(String key)
1001     {
1002         return getBigIntegerArray(key, new BigInteger[0]);
1003     }
1004 
1005     /**
1006      * Get an array of BigIntegers associated with the given
1007      * configuration key. If the key doesn't map to an existing object
1008      * an empty array is returned.
1009      *
1010      * @param key The configuration key.
1011      * @param defaultValue the default value, which will be returned if the property is not found
1012      * @return The associated BigInteger array if the key is found.
1013      *
1014      * @throws ConversionException is thrown if the key maps to an
1015      *         object that is not a list of BigIntegers.
1016      */
1017     public BigInteger[] getBigIntegerArray(String key, BigInteger[] defaultValue)
1018     {
1019         return (BigInteger[]) getArray(BigInteger.class, key, defaultValue);
1020     }
1021 
1022     /**
1023      * Get a list of BigDecimals associated with the given configuration key.
1024      * If the key doesn't map to an existing object an empty list is returned.
1025      *
1026      * @param key The configuration key.
1027      * @return The associated BigDecimal list if the key is found.
1028      *
1029      * @throws ConversionException is thrown if the key maps to an
1030      *         object that is not a list of BigDecimals.
1031      */
1032     public List<BigDecimal> getBigDecimalList(String key)
1033     {
1034         return getBigDecimalList(key, new ArrayList<BigDecimal>());
1035     }
1036 
1037     /**
1038      * Get a list of BigDecimals associated with the given configuration key.
1039      * If the key doesn't map to an existing object, the default value is
1040      * returned.
1041      *
1042      * @param key The configuration key.
1043      * @param defaultValue The default value.
1044      * @return The associated List of BigDecimals.
1045      *
1046      * @throws ConversionException is thrown if the key maps to an
1047      *         object that is not a list of BigDecimals.
1048      */
1049     public List<BigDecimal> getBigDecimalList(String key, List<BigDecimal> defaultValue)
1050     {
1051         return getList(BigDecimal.class, key, defaultValue);
1052     }
1053 
1054     /**
1055      * Get an array of BigDecimals associated with the given
1056      * configuration key. If the key doesn't map to an existing object
1057      * an empty array is returned.
1058      *
1059      * @param key The configuration key.
1060      * @return The associated BigDecimal array if the key is found.
1061      *
1062      * @throws ConversionException is thrown if the key maps to an
1063      *         object that is not a list of BigDecimals.
1064      */
1065     public BigDecimal[] getBigDecimalArray(String key)
1066     {
1067         return getBigDecimalArray(key, new BigDecimal[0]);
1068     }
1069 
1070     /**
1071      * Get an array of BigDecimals associated with the given
1072      * configuration key. If the key doesn't map to an existing object
1073      * an empty array is returned.
1074      *
1075      * @param key The configuration key.
1076      * @param defaultValue the default value, which will be returned if the property is not found
1077      * @return The associated BigDecimal array if the key is found.
1078      *
1079      * @throws ConversionException is thrown if the key maps to an
1080      *         object that is not a list of BigDecimals.
1081      */
1082     public BigDecimal[] getBigDecimalArray(String key, BigDecimal[] defaultValue)
1083     {
1084         return (BigDecimal[]) getArray(BigDecimal.class, key, defaultValue);
1085     }
1086 
1087     /**
1088      * Get an URL associated with the given configuration key.
1089      *
1090      * @param key The configuration key.
1091      * @return The associated URL.
1092      *
1093      * @throws ConversionException is thrown if the key maps to an
1094      *         object that is not an URL.
1095      */
1096     public URL getURL(String key)
1097     {
1098         return get(URL.class, key);
1099     }
1100 
1101     /**
1102      * Get an URL associated with the given configuration key.
1103      * If the key doesn't map to an existing object, the default value
1104      * is returned.
1105      *
1106      * @param key          The configuration key.
1107      * @param defaultValue The default value.
1108      * @return The associated URL.
1109      *
1110      * @throws ConversionException is thrown if the key maps to an
1111      *         object that is not an URL.
1112      */
1113     public URL getURL(String key, URL defaultValue)
1114     {
1115         return get(URL.class, key, defaultValue);
1116     }
1117 
1118     /**
1119      * Get a list of URLs associated with the given configuration key.
1120      * If the key doesn't map to an existing object an empty list is returned.
1121      *
1122      * @param key The configuration key.
1123      * @return The associated URL list if the key is found.
1124      *
1125      * @throws ConversionException is thrown if the key maps to an
1126      *         object that is not a list of URLs.
1127      */
1128     public List<URL> getURLList(String key)
1129     {
1130         return getURLList(key, new ArrayList<URL>());
1131     }
1132 
1133     /**
1134      * Get a list of URLs associated with the given configuration key.
1135      * If the key doesn't map to an existing object, the default value is
1136      * returned.
1137      *
1138      * @param key The configuration key.
1139      * @param defaultValue The default value.
1140      * @return The associated List of URLs.
1141      *
1142      * @throws ConversionException is thrown if the key maps to an
1143      *         object that is not a list of URLs.
1144      */
1145     public List<URL> getURLList(String key, List<URL> defaultValue)
1146     {
1147         return getList(URL.class, key, defaultValue);
1148     }
1149 
1150     /**
1151      * Get an array of URLs associated with the given configuration key.
1152      * If the key doesn't map to an existing object an empty array is returned.
1153      *
1154      * @param key The configuration key.
1155      * @return The associated URL array if the key is found.
1156      *
1157      * @throws ConversionException is thrown if the key maps to an
1158      *         object that is not a list of URLs.
1159      */
1160     public URL[] getURLArray(String key)
1161     {
1162         return getURLArray(key, new URL[0]);
1163     }
1164 
1165     /**
1166      * Get an array of URLs associated with the given configuration key.
1167      * If the key doesn't map to an existing object an empty array is returned.
1168      *
1169      * @param key The configuration key.
1170      * @param defaultValue the default value, which will be returned if the property is not found
1171      * @return The associated URL array if the key is found.
1172      *
1173      * @throws ConversionException is thrown if the key maps to an
1174      *         object that is not a list of URLs.
1175      */
1176     public URL[] getURLArray(String key, URL[] defaultValue)
1177     {
1178         return (URL[]) getArray(URL.class, key, defaultValue);
1179     }
1180 
1181     /**
1182      * Get a Date associated with the given configuration key. If the property
1183      * is a String, it will be parsed with the format defined by the user in
1184      * the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
1185      * {@link #DEFAULT_DATE_FORMAT} pattern.
1186      *
1187      * @param key The configuration key.
1188      * @return The associated Date.
1189      *
1190      * @throws ConversionException is thrown if the key maps to an
1191      *         object that is not a Date.
1192      */
1193     public Date getDate(String key)
1194     {
1195         return get(Date.class, key);
1196     }
1197 
1198     /**
1199      * Get a Date associated with the given configuration key. If the property
1200      * is a String, it will be parsed with the specified format pattern.
1201      *
1202      * @param key    The configuration key.
1203      * @param format The non-localized {@link java.text.DateFormat} pattern.
1204      * @return The associated Date
1205      *
1206      * @throws ConversionException is thrown if the key maps to an
1207      *         object that is not a Date.
1208      */
1209     public Date getDate(String key, String format)
1210     {
1211         Date value = getDate(key, null, format);
1212         if (value != null)
1213         {
1214             return value;
1215         }
1216         else if (isThrowExceptionOnMissing())
1217         {
1218             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
1219         }
1220         else
1221         {
1222             return null;
1223         }
1224     }
1225 
1226     /**
1227      * Get a Date associated with the given configuration key. If the property
1228      * is a String, it will be parsed with the format defined by the user in
1229      * the {@link #DATE_FORMAT_KEY} property, or if it's not defined with the
1230      * {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map to an
1231      * existing object, the default value is returned.
1232      *
1233      * @param key          The configuration key.
1234      * @param defaultValue The default value.
1235      * @return The associated Date.
1236      *
1237      * @throws ConversionException is thrown if the key maps to an
1238      *         object that is not a Date.
1239      */
1240     public Date getDate(String key, Date defaultValue)
1241     {
1242         return getDate(key, defaultValue, getDefaultDateFormat());
1243     }
1244 
1245     /**
1246      * Get a Date associated with the given configuration key. If the property
1247      * is a String, it will be parsed with the specified format pattern.
1248      * If the key doesn't map to an existing object, the default value
1249      * is returned.
1250      *
1251      * @param key          The configuration key.
1252      * @param defaultValue The default value.
1253      * @param format       The non-localized {@link java.text.DateFormat} pattern.
1254      * @return The associated Date.
1255      *
1256      * @throws ConversionException is thrown if the key maps to an
1257      *         object that is not a Date.
1258      */
1259     public Date getDate(String key, Date defaultValue, String format)
1260     {
1261         Object value = resolveContainerStore(key);
1262 
1263         if (value == null)
1264         {
1265             return defaultValue;
1266         }
1267         else
1268         {
1269             try
1270             {
1271                 return PropertyConverter.toDate(interpolate(value), format);
1272             }
1273             catch (ConversionException e)
1274             {
1275                 throw new ConversionException('\'' + key + "' doesn't map to a Date", e);
1276             }
1277         }
1278     }
1279     public List<Date> getDateList(String key)
1280     {
1281         return getDateList(key, new ArrayList<Date>());
1282     }
1283 
1284     /**
1285      * Get a list of Dates associated with the given configuration key.
1286      * If the property is a list of Strings, they will be parsed with the
1287      * specified format pattern. If the key doesn't map to an existing object
1288      * an empty list is returned.
1289      *
1290      * @param key    The configuration key.
1291      * @param format The non-localized {@link java.text.DateFormat} pattern.
1292      * @return The associated Date list if the key is found.
1293      *
1294      * @throws ConversionException is thrown if the key maps to an
1295      *         object that is not a list of Dates.
1296      */
1297     public List<Date> getDateList(String key, String format)
1298     {
1299         return getDateList(key, new ArrayList<Date>(), format);
1300     }
1301 
1302     /**
1303      * Get a list of Dates associated with the given configuration key.
1304      * If the property is a list of Strings, they will be parsed with the
1305      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1306      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1307      * If the key doesn't map to an existing object, the default value is
1308      * returned.
1309      *
1310      * @param key          The configuration key.
1311      * @param defaultValue The default value.
1312      * @return The associated Date list if the key is found.
1313      *
1314      * @throws ConversionException is thrown if the key maps to an
1315      *         object that is not a list of Dates.
1316      */
1317     public List<Date> getDateList(String key, List<Date> defaultValue)
1318     {
1319         return getDateList(key, defaultValue, getDefaultDateFormat());
1320     }
1321 
1322     /**
1323      * Get a list of Dates associated with the given configuration key.
1324      * If the property is a list of Strings, they will be parsed with the
1325      * specified format pattern. If the key doesn't map to an existing object,
1326      * the default value is returned.
1327      *
1328      * @param key          The configuration key.
1329      * @param defaultValue The default value.
1330      * @param format       The non-localized {@link java.text.DateFormat} pattern.
1331      * @return The associated Date list if the key is found.
1332      *
1333      * @throws ConversionException is thrown if the key maps to an
1334      *         object that is not a list of Dates.
1335      */
1336     public List<Date> getDateList(String key, List<Date> defaultValue, String format)
1337     {
1338         Object value = getProperty(key);
1339 
1340         List<Date> list;
1341 
1342         if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
1343         {
1344             list = defaultValue;
1345         }
1346         else if (value.getClass().isArray())
1347         {
1348             list = new ArrayList<Date>();
1349             int length = Array.getLength(value);
1350             for (int i = 0; i < length; i++)
1351             {
1352                 list.add(convert(Date.class, key, interpolate(Array.get(value, i)), new String[] {format}));
1353             }
1354         }
1355         else if (value instanceof Collection)
1356         {
1357             Collection<?> values = (Collection<?>) value;
1358             list = new ArrayList<Date>();
1359 
1360             for (Object o : values)
1361             {
1362                 list.add(convert(Date.class, key, interpolate(o), new String[] {format}));
1363             }
1364         }
1365         else
1366         {
1367             // attempt to convert a single value
1368             list = new ArrayList<Date>();
1369             list.add(convert(Date.class, key, interpolate(value), new String[] {format}));
1370         }
1371 
1372         return list;
1373     }
1374 
1375     /**
1376      * Get an array of Dates associated with the given configuration key.
1377      * If the property is a list of Strings, they will be parsed with the
1378      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1379      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1380      * If the key doesn't map to an existing object an empty array is returned.
1381      *
1382      * @param key The configuration key.
1383      * @return The associated Date array if the key is found.
1384      *
1385      * @throws ConversionException is thrown if the key maps to an
1386      *         object that is not a list of Dates.
1387      */
1388     public Date[] getDateArray(String key)
1389     {
1390         return getDateArray(key, new Date[0]);
1391     }
1392 
1393     /**
1394      * Get an array of Dates associated with the given configuration key.
1395      * If the property is a list of Strings, they will be parsed with the
1396      * specified format pattern. If the key doesn't map to an existing object
1397      * an empty array is returned.
1398      *
1399      * @param key    The configuration key.
1400      * @param format The non-localized {@link java.text.DateFormat} pattern.
1401      * @return The associated Date array if the key is found.
1402      *
1403      * @throws ConversionException is thrown if the key maps to an
1404      *         object that is not a list of Dates.
1405      */
1406     public Date[] getDateArray(String key, String format)
1407     {
1408         return getDateArray(key, new Date[0], format);
1409     }
1410 
1411     /**
1412      * Get an array of Dates associated with the given configuration key.
1413      * If the property is a list of Strings, they will be parsed with the
1414      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1415      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1416      * If the key doesn't map to an existing object an empty array is returned.
1417      *
1418      * @param key The configuration key.
1419      * @param defaultValue the default value, which will be returned if the property is not found
1420      * @return The associated Date array if the key is found.
1421      *
1422      * @throws ConversionException is thrown if the key maps to an
1423      *         object that is not a list of Dates.
1424      */
1425     public Date[] getDateArray(String key, Date[] defaultValue)
1426     {
1427         return getDateArray(key, defaultValue, getDefaultDateFormat());
1428     }
1429 
1430     /**
1431      * Get an array of Dates associated with the given configuration key.
1432      * If the property is a list of Strings, they will be parsed with the
1433      * specified format pattern. If the key doesn't map to an existing object,
1434      * the default value is returned.
1435      *
1436      * @param key          The configuration key.
1437      * @param defaultValue The default value.
1438      * @param format       The non-localized {@link java.text.DateFormat} pattern.
1439      * @return The associated Date array if the key is found.
1440      *
1441      * @throws ConversionException is thrown if the key maps to an
1442      *         object that is not a list of Dates.
1443      */
1444     public Date[] getDateArray(String key, Date[] defaultValue, String format)
1445     {
1446         List<Date> list = getDateList(key, format);
1447         if (list.isEmpty())
1448         {
1449             return defaultValue;
1450         }
1451         else
1452         {
1453             return list.toArray(new Date[list.size()]);
1454         }
1455     }
1456 
1457     /**
1458      * Get a Calendar associated with the given configuration key. If the
1459      * property is a String, it will be parsed with the format defined by the
1460      * user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
1461      * with the {@link #DEFAULT_DATE_FORMAT} pattern.
1462      *
1463      * @param key The configuration key.
1464      * @return The associated Calendar.
1465      *
1466      * @throws ConversionException is thrown if the key maps to an
1467      *         object that is not a Calendar.
1468      */
1469     public Calendar getCalendar(String key)
1470     {
1471         return get(Calendar.class, key);
1472     }
1473 
1474     /**
1475      * Get a Calendar associated with the given configuration key. If the
1476      * property is a String, it will be parsed with the specified format
1477      * pattern.
1478      *
1479      * @param key    The configuration key.
1480      * @param format The non-localized {@link java.text.DateFormat} pattern.
1481      * @return The associated Calendar
1482      *
1483      * @throws ConversionException is thrown if the key maps to an
1484      *         object that is not a Calendar.
1485      */
1486     public Calendar getCalendar(String key, String format)
1487     {
1488         Calendar value = getCalendar(key, null, format);
1489         if (value != null)
1490         {
1491             return value;
1492         }
1493         else if (isThrowExceptionOnMissing())
1494         {
1495             throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
1496         }
1497         else
1498         {
1499             return null;
1500         }
1501     }
1502 
1503     /**
1504      * Get a Calendar associated with the given configuration key. If the
1505      * property is a String, it will be parsed with the format defined by the
1506      * user in the {@link #DATE_FORMAT_KEY} property, or if it's not defined
1507      * with the {@link #DEFAULT_DATE_FORMAT} pattern. If the key doesn't map
1508      * to an existing object, the default value is returned.
1509      *
1510      * @param key          The configuration key.
1511      * @param defaultValue The default value.
1512      * @return The associated Calendar.
1513      *
1514      * @throws ConversionException is thrown if the key maps to an
1515      *         object that is not a Calendar.
1516      */
1517     public Calendar getCalendar(String key, Calendar defaultValue)
1518     {
1519         return getCalendar(key, defaultValue, getDefaultDateFormat());
1520     }
1521 
1522     /**
1523      * Get a Calendar associated with the given configuration key. If the
1524      * property is a String, it will be parsed with the specified format
1525      * pattern. If the key doesn't map to an existing object, the default
1526      * value is returned.
1527      *
1528      * @param key          The configuration key.
1529      * @param defaultValue The default value.
1530      * @param format       The non-localized {@link java.text.DateFormat} pattern.
1531      * @return The associated Calendar.
1532      *
1533      * @throws ConversionException is thrown if the key maps to an
1534      *         object that is not a Calendar.
1535      */
1536     public Calendar getCalendar(String key, Calendar defaultValue, String format)
1537     {
1538         Object value = resolveContainerStore(key);
1539 
1540         if (value == null)
1541         {
1542             return defaultValue;
1543         }
1544         else
1545         {
1546             try
1547             {
1548                 return PropertyConverter.toCalendar(interpolate(value), format);
1549             }
1550             catch (ConversionException e)
1551             {
1552                 throw new ConversionException('\'' + key + "' doesn't map to a Calendar", e);
1553             }
1554         }
1555     }
1556 
1557     /**
1558      * Get a list of Calendars associated with the given configuration key.
1559      * If the property is a list of Strings, they will be parsed with the
1560      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1561      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1562      * If the key doesn't map to an existing object an empty list is returned.
1563      *
1564      * @param key The configuration key.
1565      * @return The associated Calendar list if the key is found.
1566      *
1567      * @throws ConversionException is thrown if the key maps to an
1568      *         object that is not a list of Calendars.
1569      */
1570     public List<Calendar> getCalendarList(String key)
1571     {
1572         return getCalendarList(key, new ArrayList<Calendar>());
1573     }
1574 
1575     /**
1576      * Get a list of Calendars associated with the given configuration key.
1577      * If the property is a list of Strings, they will be parsed with the
1578      * specified format pattern. If the key doesn't map to an existing object
1579      * an empty list is returned.
1580      *
1581      * @param key    The configuration key.
1582      * @param format The non-localized {@link java.text.DateFormat} pattern.
1583      * @return The associated Calendar list if the key is found.
1584      *
1585      * @throws ConversionException is thrown if the key maps to an
1586      *         object that is not a list of Calendars.
1587      */
1588     public List<Calendar> getCalendarList(String key, String format)
1589     {
1590         return getCalendarList(key, new ArrayList<Calendar>(), format);
1591     }
1592 
1593     /**
1594      * Get a list of Calendars associated with the given configuration key.
1595      * If the property is a list of Strings, they will be parsed with the
1596      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1597      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1598      * If the key doesn't map to an existing object, the default value is
1599      * returned.
1600      *
1601      * @param key The configuration key.
1602      * @param defaultValue The default value.
1603      * @return The associated Calendar list if the key is found.
1604      *
1605      * @throws ConversionException is thrown if the key maps to an
1606      *         object that is not a list of Calendars.
1607      */
1608     public List<Calendar> getCalendarList(String key, List<Calendar> defaultValue)
1609     {
1610         return getCalendarList(key, defaultValue, getDefaultDateFormat());
1611     }
1612 
1613     /**
1614      * Get a list of Calendars associated with the given configuration key.
1615      * If the property is a list of Strings, they will be parsed with the
1616      * specified format pattern. If the key doesn't map to an existing object,
1617      * the default value is returned.
1618      *
1619      * @param key          The configuration key.
1620      * @param defaultValue The default value.
1621      * @param format       The non-localized {@link java.text.DateFormat} pattern.
1622      * @return The associated Calendar 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 Calendars.
1626      */
1627     public List<Calendar> getCalendarList(String key, List<Calendar> defaultValue, String format)
1628     {
1629         Object value = getProperty(key);
1630 
1631         List<Calendar> list;
1632 
1633         if (value == null || (value instanceof String && StringUtils.isEmpty((String) value)))
1634         {
1635             list = defaultValue;
1636         }
1637         else if (value.getClass().isArray())
1638         {
1639             list = new ArrayList<Calendar>();
1640             int length = Array.getLength(value);
1641             for (int i = 0; i < length; i++)
1642             {
1643                 list.add(convert(Calendar.class, key, interpolate(Array.get(value, i)), new String[] {format}));
1644             }
1645         }
1646         else if (value instanceof Collection)
1647         {
1648             Collection<?> values = (Collection<?>) value;
1649             list = new ArrayList<Calendar>();
1650 
1651             for (Object o : values)
1652             {
1653                 list.add(convert(Calendar.class, key, interpolate(o), new String[] {format}));
1654             }
1655         }
1656         else
1657         {
1658             // attempt to convert a single value
1659             list = new ArrayList<Calendar>();
1660             list.add(convert(Calendar.class, key, interpolate(value), new String[] {format}));
1661         }
1662 
1663         return list;
1664     }
1665 
1666     /**
1667      * Get an array of Calendars associated with the given configuration key.
1668      * If the property is a list of Strings, they will be parsed with the
1669      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1670      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1671      * If the key doesn't map to an existing object an empty array is returned.
1672      *
1673      * @param key The configuration key.
1674      * @return The associated Calendar 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 Calendars.
1678      */
1679     public Calendar[] getCalendarArray(String key)
1680     {
1681         return getCalendarArray(key, new Calendar[0]);
1682     }
1683 
1684     /**
1685      * Get an array of Calendars associated with the given configuration key.
1686      * If the property is a list of Strings, they will be parsed with the
1687      * specified format pattern. If the key doesn't map to an existing object
1688      * an empty array is returned.
1689      *
1690      * @param key    The configuration key.
1691      * @param format The non-localized {@link java.text.DateFormat} pattern.
1692      * @return The associated Calendar array if the key is found.
1693      *
1694      * @throws ConversionException is thrown if the key maps to an
1695      *         object that is not a list of Calendars.
1696      */
1697     public Calendar[] getCalendarArray(String key, String format)
1698     {
1699         return getCalendarArray(key, new Calendar[0], format);
1700     }
1701 
1702     /**
1703      * Get an array of Calendars associated with the given configuration key.
1704      * If the property is a list of Strings, they will be parsed with the
1705      * format defined by the user in the {@link #DATE_FORMAT_KEY} property,
1706      * or if it's not defined with the {@link #DEFAULT_DATE_FORMAT} pattern.
1707      * If the key doesn't map to an existing object an empty array is returned.
1708      *
1709      * @param key The configuration key.
1710      * @param defaultValue the default value, which will be returned if the property is not found
1711      * @return The associated Calendar array if the key is found.
1712      *
1713      * @throws ConversionException is thrown if the key maps to an
1714      *         object that is not a list of Calendars.
1715      */
1716     public Calendar[] getCalendarArray(String key, Calendar[] defaultValue)
1717     {
1718         return getCalendarArray(key, defaultValue, getDefaultDateFormat());
1719     }
1720 
1721     /**
1722      * Get an array of Calendars associated with the given configuration key.
1723      * If the property is a list of Strings, they will be parsed with the
1724      * specified format pattern. If the key doesn't map to an existing object,
1725      * the default value is returned.
1726      *
1727      * @param key          The configuration key.
1728      * @param defaultValue The default value.
1729      * @param format       The non-localized {@link java.text.DateFormat} pattern.
1730      * @return The associated Calendar array if the key is found.
1731      *
1732      * @throws ConversionException is thrown if the key maps to an
1733      *         object that is not a list of Calendars.
1734      */
1735     public Calendar[] getCalendarArray(String key, Calendar[] defaultValue, String format)
1736     {
1737         List<Calendar> list = getCalendarList(key, format);
1738         if (list.isEmpty())
1739         {
1740             return defaultValue;
1741         }
1742         else
1743         {
1744             return list.toArray(new Calendar[list.size()]);
1745         }
1746     }
1747 
1748     /**
1749      * Returns the date format specified by the user in the DATE_FORMAT_KEY
1750      * property, or the default format otherwise.
1751      *
1752      * @return the default date format
1753      */
1754     private String getDefaultDateFormat()
1755     {
1756         return getString(DATE_FORMAT_KEY, DEFAULT_DATE_FORMAT);
1757     }
1758 
1759     /**
1760      * Get a Locale associated with the given configuration key.
1761      *
1762      * @param key The configuration key.
1763      * @return The associated Locale.
1764      *
1765      * @throws ConversionException is thrown if the key maps to an
1766      *         object that is not a Locale.
1767      */
1768     public Locale getLocale(String key)
1769     {
1770         return get(Locale.class, key);
1771     }
1772 
1773     /**
1774      * Get a Locale associated with the given configuration key.
1775      * If the key doesn't map to an existing object, the default value
1776      * is returned.
1777      *
1778      * @param key          The configuration key.
1779      * @param defaultValue The default value.
1780      * @return The associated Locale.
1781      *
1782      * @throws ConversionException is thrown if the key maps to an
1783      *         object that is not a Locale.
1784      */
1785     public Locale getLocale(String key, Locale defaultValue)
1786     {
1787         return get(Locale.class, key, defaultValue);
1788     }
1789 
1790     /**
1791      * Get a list of Locales associated with the given configuration key.
1792      * If the key doesn't map to an existing object an empty list is returned.
1793      *
1794      * @param key The configuration key.
1795      * @return The associated Locale list if the key is found.
1796      *
1797      * @throws ConversionException is thrown if the key maps to an
1798      *         object that is not a list of Locales.
1799      */
1800     public List<Locale> getLocaleList(String key)
1801     {
1802         return getLocaleList(key, new ArrayList<Locale>());
1803     }
1804 
1805     /**
1806      * Get a list of Locales associated with the given configuration key.
1807      * If the key doesn't map to an existing object, the default value is
1808      * returned.
1809      *
1810      * @param key The configuration key.
1811      * @param defaultValue The default value.
1812      * @return The associated List of Locales.
1813      *
1814      * @throws ConversionException is thrown if the key maps to an
1815      *         object that is not a list of Locales.
1816      */
1817     public List<Locale> getLocaleList(String key, List<Locale> defaultValue)
1818     {
1819         return getList(Locale.class, key, defaultValue);
1820     }
1821 
1822     /**
1823      * Get an array of Locales associated with the given
1824      * configuration key. If the key doesn't map to an existing object
1825      * an empty array is returned.
1826      *
1827      * @param key The configuration key.
1828      * @return The associated Locale array if the key is found.
1829      *
1830      * @throws ConversionException is thrown if the key maps to an
1831      *         object that is not a list of Locales.
1832      */
1833     public Locale[] getLocaleArray(String key)
1834     {
1835         return getLocaleArray(key, new Locale[0]);
1836     }
1837 
1838     /**
1839      * Get an array of Locales associated with the given
1840      * configuration key. If the key doesn't map to an existing object
1841      * an empty array is returned.
1842      *
1843      * @param key The configuration key.
1844      * @param defaultValue the default value, which will be returned if the property is not found
1845      * @return The associated Locale array if the key is found.
1846      *
1847      * @throws ConversionException is thrown if the key maps to an
1848      *         object that is not a list of Locales.
1849      */
1850     public Locale[] getLocaleArray(String key, Locale[] defaultValue)
1851     {
1852         return (Locale[]) getArray(Locale.class, key, defaultValue);
1853     }
1854 
1855     /**
1856      * Get a Color associated with the given configuration key.
1857      *
1858      * @param key The configuration key.
1859      * @return The associated Color.
1860      *
1861      * @throws ConversionException is thrown if the key maps to an
1862      *         object that is not a Color.
1863      */
1864     public Color getColor(String key)
1865     {
1866         return get(Color.class, key);
1867     }
1868 
1869     /**
1870      * Get a Color associated with the given configuration key.
1871      * If the key doesn't map to an existing object, the default value
1872      * is returned.
1873      *
1874      * @param key          The configuration key.
1875      * @param defaultValue The default value.
1876      * @return The associated Color.
1877      *
1878      * @throws ConversionException is thrown if the key maps to an
1879      *         object that is not a Color.
1880      */
1881     public Color getColor(String key, Color defaultValue)
1882     {
1883         return get(Color.class, key, defaultValue);
1884     }
1885 
1886     /**
1887      * Get a list of Colors associated with the given configuration key.
1888      * If the key doesn't map to an existing object an empty list is returned.
1889      *
1890      * @param key The configuration key.
1891      * @return The associated Color list if the key is found.
1892      *
1893      * @throws ConversionException is thrown if the key maps to an
1894      *         object that is not a list of Colors.
1895      */
1896     public List<Color> getColorList(String key)
1897     {
1898         return getColorList(key, new ArrayList<Color>());
1899     }
1900 
1901     /**
1902      * Get a list of Colors associated with the given configuration key.
1903      * If the key doesn't map to an existing object, the default value is
1904      * returned.
1905      *
1906      * @param key The configuration key.
1907      * @param defaultValue The default value.
1908      * @return The associated List of Colors.
1909      *
1910      * @throws ConversionException is thrown if the key maps to an
1911      *         object that is not a list of Colors.
1912      */
1913     public List<Color> getColorList(String key, List<Color> defaultValue)
1914     {
1915         return getList(Color.class, key, defaultValue);
1916     }
1917 
1918     /**
1919      * Get an array of Colors associated with the given
1920      * configuration key. If the key doesn't map to an existing object
1921      * an empty array is returned.
1922      *
1923      * @param key The configuration key.
1924      * @return The associated Color array if the key is found.
1925      *
1926      * @throws ConversionException is thrown if the key maps to an
1927      *         object that is not a list of Colors.
1928      */
1929     public Color[] getColorArray(String key)
1930     {
1931         return getColorArray(key, new Color[0]);
1932     }
1933 
1934     /**
1935      * Get an array of Colors associated with the given
1936      * configuration key. If the key doesn't map to an existing object
1937      * an empty array is returned.
1938      *
1939      * @param key The configuration key.
1940      * @param defaultValue the default value, which will be returned if the property is not found
1941      * @return The associated Color array if the key is found.
1942      *
1943      * @throws ConversionException is thrown if the key maps to an
1944      *         object that is not a list of Colors.
1945      */
1946     public Color[] getColorArray(String key, Color[] defaultValue)
1947     {
1948         return (Color[]) getArray(Color.class, key, defaultValue);
1949     }
1950 
1951     /**
1952      * Helper method for performing a type conversion using the
1953      * {@code PropertyConverter} class.
1954      *
1955      * @param <T> the target type of the conversion
1956      * @param cls the target class of the conversion
1957      * @param key the configuration key
1958      * @param value the value to be converted
1959      * @param params additional parameters
1960      * @throws ConversionException if the value is not compatible with the
1961      *         requested type
1962      */
1963     private static <T> T convert(Class<T> cls, String key, Object value,
1964             Object[] params)
1965     {
1966         try
1967         {
1968             Object result = PropertyConverter.to(cls, value, params);
1969             // Will not throw a ClassCastException because PropertyConverter
1970             // would have thrown a ConversionException if conversion had failed.
1971             return cls.cast(result);
1972         }
1973         catch (ConversionException e)
1974         {
1975             throw new ConversionException('\'' + key + "' doesn't map to a "
1976                     + cls, e);
1977         }
1978     }
1979 }