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