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.lang.reflect.Array;
    22  import java.lang.reflect.Constructor;
    23  import java.lang.reflect.InvocationTargetException;
    24  import java.lang.reflect.Method;
    25  import java.math.BigDecimal;
    26  import java.math.BigInteger;
    27  import java.net.InetAddress;
    28  import java.net.MalformedURLException;
    29  import java.net.URL;
    30  import java.net.UnknownHostException;
    31  import java.text.ParseException;
    32  import java.text.SimpleDateFormat;
    33  import java.util.ArrayList;
    34  import java.util.Calendar;
    35  import java.util.Collection;
    36  import java.util.Date;
    37  import java.util.Iterator;
    38  import java.util.List;
    39  import java.util.Locale;
    40
    41  import org.apache.commons.collections.IteratorUtils;
    42  import org.apache.commons.collections.iterators.IteratorChain;
    43  import org.apache.commons.collections.iterators.SingletonIterator;
    44  import org.apache.commons.lang.BooleanUtils;
    45  import org.apache.commons.lang.StringUtils;
    46  import org.apache.commons.lang.SystemUtils;
    47
    48  /***
    49   * A utility class to convert the configuration properties into any type.
    50   *
    51   * @author Emmanuel Bourg
    52   * @version $Revision: 662269 $, $Date: 2008-06-01 21:30:02 +0200 (So, 01 Jun 2008) $
    53   * @since 1.1
    54   */
    55  public final class PropertyConverter
    56  {
    57      /*** Constant for the list delimiter as char.*/
    58      static final char LIST_ESC_CHAR = '//';
    59
    60      /*** Constant for the list delimiter escaping character as string.*/
    61      static final String LIST_ESCAPE = String.valueOf(LIST_ESC_CHAR);
    62
    63      /*** Constant for the prefix of hex numbers.*/
    64      private static final String HEX_PREFIX = "0x";
    65
    66      /*** Constant for the radix of hex numbers.*/
    67      private static final int HEX_RADIX = 16;
    68
    69      /*** Constant for the Java version 1.5.*/
    70      private static final float JAVA_VERSION_1_5 = 1.5f;
    71
    72      /*** Constant for the argument classes of the Number constructor that takes a String. */
    73      private static final Class[] CONSTR_ARGS = {String.class};
    74
    75      /*** The fully qualified name of {@link javax.mail.internet.InternetAddress} */
    76      private static final String INTERNET_ADDRESS_CLASSNAME = "javax.mail.internet.InternetAddress";
    77
    78      /***
    79       * Private constructor prevents instances from being created.
    80       */
    81      private PropertyConverter()
    82      {
    83          // to prevent instanciation...
    84      }
    85
    86      /***
    87       * Converts the specified value to the target class. If the class is a
    88       * primitive type (Integer.TYPE, Boolean.TYPE, etc) the value returned
    89       * will use the wrapper type (Integer.class, Boolean.class, etc).
    90       *
    91       * @param cls   the target class of the converted value
    92       * @param value the value to convert
    93       * @param params optional parameters used for the conversion
    94       * @return the converted value
    95       * @throws ConversionException if the value is not compatible with the requested type
    96       *
    97       * @since 1.5
    98       */
    99      static Object to(Class cls, Object value, Object[] params) throws ConversionException
    100     {
    101         if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls))
    102         {
    103             return toBoolean(value);
    104         }
    105         else if (Number.class.isAssignableFrom(cls) || cls.isPrimitive())
    106         {
    107             if (Integer.class.equals(cls) || Integer.TYPE.equals(cls))
    108             {
    109                 return toInteger(value);
    110             }
    111             else if (Long.class.equals(cls) || Long.TYPE.equals(cls))
    112             {
    113                 return toLong(value);
    114             }
    115             else if (Byte.class.equals(cls) || Byte.TYPE.equals(cls))
    116             {
    117                 return toByte(value);
    118             }
    119             else if (Short.class.equals(cls) || Short.TYPE.equals(cls))
    120             {
    121                 return toShort(value);
    122             }
    123             else if (Float.class.equals(cls) || Float.TYPE.equals(cls))
    124             {
    125                 return toFloat(value);
    126             }
    127             else if (Double.class.equals(cls) || Double.TYPE.equals(cls))
    128             {
    129                 return toDouble(value);
    130             }
    131             else if (BigInteger.class.equals(cls))
    132             {
    133                 return toBigInteger(value);
    134             }
    135             else if (BigDecimal.class.equals(cls))
    136             {
    137                 return toBigDecimal(value);
    138             }
    139         }
    140         else if (Date.class.equals(cls))
    141         {
    142             return toDate(value, (String) params[0]);
    143         }
    144         else if (Calendar.class.equals(cls))
    145         {
    146             return toCalendar(value, (String) params[0]);
    147         }
    148         else if (URL.class.equals(cls))
    149         {
    150             return toURL(value);
    151         }
    152         else if (Locale.class.equals(cls))
    153         {
    154             return toLocale(value);
    155         }
    156         else if (isEnum(cls))
    157         {
    158             return toEnum(value, cls);
    159         }
    160         else if (Color.class.equals(cls))
    161         {
    162             return toColor(value);
    163         }
    164         else if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME))
    165         {
    166             return toInternetAddress(value);
    167         }
    168         else if (InetAddress.class.isAssignableFrom(cls))
    169         {
    170             return toInetAddress(value);
    171         }
    172
    173         throw new ConversionException("The value '" + value + "' (" + value.getClass() + ")"
    174                 + " can't be converted to a " + cls.getName() + " object");
    175     }
    176
    177     /***
    178      * Convert the specified object into a Boolean. Internally the
    179      * <code>org.apache.commons.lang.BooleanUtils</code> class from the
    180      * <a href="http://commons.apache.org/lang/">Commons Lang</a>
    181      * project is used to perform this conversion. This class accepts some more
    182      * tokens for the boolean value of <b>true</b>, e.g. <code>yes</code> and
    183      * <code>on</code>. Please refer to the documentation of this class for more
    184      * details.
    185      *
    186      * @param value the value to convert
    187      * @return the converted value
    188      * @throws ConversionException thrown if the value cannot be converted to a boolean
    189      */
    190     public static Boolean toBoolean(Object value) throws ConversionException
    191     {
    192         if (value instanceof Boolean)
    193         {
    194             return (Boolean) value;
    195         }
    196         else if (value instanceof String)
    197         {
    198             Boolean b = BooleanUtils.toBooleanObject((String) value);
    199             if (b == null)
    200             {
    201                 throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
    202             }
    203             return b;
    204         }
    205         else
    206         {
    207             throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
    208         }
    209     }
    210
    211     /***
    212      * Convert the specified object into a Byte.
    213      *
    214      * @param value the value to convert
    215      * @return the converted value
    216      * @throws ConversionException thrown if the value cannot be converted to a byte
    217      */
    218     public static Byte toByte(Object value) throws ConversionException
    219     {
    220         Number n = toNumber(value, Byte.class);
    221         if (n instanceof Byte)
    222         {
    223             return (Byte) n;
    224         }
    225         else
    226         {
    227             return new Byte(n.byteValue());
    228         }
    229     }
    230
    231     /***
    232      * Convert the specified object into a Short.
    233      *
    234      * @param value the value to convert
    235      * @return the converted value
    236      * @throws ConversionException thrown if the value cannot be converted to a short
    237      */
    238     public static Short toShort(Object value) throws ConversionException
    239     {
    240         Number n = toNumber(value, Short.class);
    241         if (n instanceof Short)
    242         {
    243             return (Short) n;
    244         }
    245         else
    246         {
    247             return new Short(n.shortValue());
    248         }
    249     }
    250
    251     /***
    252      * Convert the specified object into an Integer.
    253      *
    254      * @param value the value to convert
    255      * @return the converted value
    256      * @throws ConversionException thrown if the value cannot be converted to an integer
    257      */
    258     public static Integer toInteger(Object value) throws ConversionException
    259     {
    260         Number n = toNumber(value, Integer.class);
    261         if (n instanceof Integer)
    262         {
    263             return (Integer) n;
    264         }
    265         else
    266         {
    267             return new Integer(n.intValue());
    268         }
    269     }
    270
    271     /***
    272      * Convert the specified object into a Long.
    273      *
    274      * @param value the value to convert
    275      * @return the converted value
    276      * @throws ConversionException thrown if the value cannot be converted to a Long
    277      */
    278     public static Long toLong(Object value) throws ConversionException
    279     {
    280         Number n = toNumber(value, Long.class);
    281         if (n instanceof Long)
    282         {
    283             return (Long) n;
    284         }
    285         else
    286         {
    287             return new Long(n.longValue());
    288         }
    289     }
    290
    291     /***
    292      * Convert the specified object into a Float.
    293      *
    294      * @param value the value to convert
    295      * @return the converted value
    296      * @throws ConversionException thrown if the value cannot be converted to a Float
    297      */
    298     public static Float toFloat(Object value) throws ConversionException
    299     {
    300         Number n = toNumber(value, Float.class);
    301         if (n instanceof Float)
    302         {
    303             return (Float) n;
    304         }
    305         else
    306         {
    307             return new Float(n.floatValue());
    308         }
    309     }
    310
    311     /***
    312      * Convert the specified object into a Double.
    313      *
    314      * @param value the value to convert
    315      * @return the converted value
    316      * @throws ConversionException thrown if the value cannot be converted to a Double
    317      */
    318     public static Double toDouble(Object value) throws ConversionException
    319     {
    320         Number n = toNumber(value, Double.class);
    321         if (n instanceof Double)
    322         {
    323             return (Double) n;
    324         }
    325         else
    326         {
    327             return new Double(n.doubleValue());
    328         }
    329     }
    330
    331     /***
    332      * Convert the specified object into a BigInteger.
    333      *
    334      * @param value the value to convert
    335      * @return the converted value
    336      * @throws ConversionException thrown if the value cannot be converted to a BigInteger
    337      */
    338     public static BigInteger toBigInteger(Object value) throws ConversionException
    339     {
    340         Number n = toNumber(value, BigInteger.class);
    341         if (n instanceof BigInteger)
    342         {
    343             return (BigInteger) n;
    344         }
    345         else
    346         {
    347             return BigInteger.valueOf(n.longValue());
    348         }
    349     }
    350
    351     /***
    352      * Convert the specified object into a BigDecimal.
    353      *
    354      * @param value the value to convert
    355      * @return the converted value
    356      * @throws ConversionException thrown if the value cannot be converted to a BigDecimal
    357      */
    358     public static BigDecimal toBigDecimal(Object value) throws ConversionException
    359     {
    360         Number n = toNumber(value, BigDecimal.class);
    361         if (n instanceof BigDecimal)
    362         {
    363             return (BigDecimal) n;
    364         }
    365         else
    366         {
    367             return new BigDecimal(n.doubleValue());
    368         }
    369     }
    370
    371     /***
    372      * Tries to convert the specified object into a number object. This method
    373      * is used by the conversion methods for number types. Note that the return
    374      * value is not in always of the specified target class, but only if a new
    375      * object has to be created.
    376      *
    377      * @param value the value to be converted (must not be <b>null</b>)
    378      * @param targetClass the target class of the conversion (must be derived
    379      * from <code>java.lang.Number</code>)
    380      * @return the converted number
    381      * @throws ConversionException if the object cannot be converted
    382      */
    383     static Number toNumber(Object value, Class targetClass) throws ConversionException
    384     {
    385         if (value instanceof Number)
    386         {
    387             return (Number) value;
    388         }
    389         else
    390         {
    391             String str = value.toString();
    392             if (str.startsWith(HEX_PREFIX))
    393             {
    394                 try
    395                 {
    396                     return new BigInteger(str.substring(HEX_PREFIX.length()), HEX_RADIX);
    397                 }
    398                 catch (NumberFormatException nex)
    399                 {
    400                     throw new ConversionException("Could not convert " + str
    401                             + " to " + targetClass.getName()
    402                             + "! Invalid hex number.", nex);
    403                 }
    404             }
    405
    406             try
    407             {
    408                 Constructor constr = targetClass.getConstructor(CONSTR_ARGS);
    409                 return (Number) constr.newInstance(new Object[]{str});
    410             }
    411             catch (InvocationTargetException itex)
    412             {
    413                 throw new ConversionException("Could not convert " + str
    414                         + " to " + targetClass.getName(), itex
    415                         .getTargetException());
    416             }
    417             catch (Exception ex)
    418             {
    419                 // Treat all possible exceptions the same way
    420                 throw new ConversionException(
    421                         "Conversion error when trying to convert " + str
    422                                 + " to " + targetClass.getName(), ex);
    423             }
    424         }
    425     }
    426
    427     /***
    428      * Convert the specified object into an URL.
    429      *
    430      * @param value the value to convert
    431      * @return the converted value
    432      * @throws ConversionException thrown if the value cannot be converted to an URL
    433      */
    434     public static URL toURL(Object value) throws ConversionException
    435     {
    436         if (value instanceof URL)
    437         {
    438             return (URL) value;
    439         }
    440         else if (value instanceof String)
    441         {
    442             try
    443             {
    444                 return new URL((String) value);
    445             }
    446             catch (MalformedURLException e)
    447             {
    448                 throw new ConversionException("The value " + value + " can't be converted to an URL", e);
    449             }
    450         }
    451         else
    452         {
    453             throw new ConversionException("The value " + value + " can't be converted to an URL");
    454         }
    455     }
    456
    457     /***
    458      * Convert the specified object into a Locale.
    459      *
    460      * @param value the value to convert
    461      * @return the converted value
    462      * @throws ConversionException thrown if the value cannot be converted to a Locale
    463      */
    464     public static Locale toLocale(Object value) throws ConversionException
    465     {
    466         if (value instanceof Locale)
    467         {
    468             return (Locale) value;
    469         }
    470         else if (value instanceof String)
    471         {
    472             List elements = split((String) value, '_');
    473             int size = elements.size();
    474
    475             if (size >= 1 && (((String) elements.get(0)).length() == 2 || ((String) elements.get(0)).length() == 0))
    476             {
    477                 String language = (String) elements.get(0);
    478                 String country = (String) ((size >= 2) ? elements.get(1) : "");
    479                 String variant = (String) ((size >= 3) ? elements.get(2) : "");
    480
    481                 return new Locale(language, country, variant);
    482             }
    483             else
    484             {
    485                 throw new ConversionException("The value " + value + " can't be converted to a Locale");
    486             }
    487         }
    488         else
    489         {
    490             throw new ConversionException("The value " + value + " can't be converted to a Locale");
    491         }
    492     }
    493
    494     /***
    495      * Split a string on the specified delimiter. To be removed when
    496      * commons-lang has a better replacement available (Tokenizer?).
    497      *
    498      * todo: replace with a commons-lang equivalent
    499      *
    500      * @param s          the string to split
    501      * @param delimiter  the delimiter
    502      * @param trim       a flag whether the single elements should be trimmed
    503      * @return a list with the single tokens
    504      */
    505     public static List split(String s, char delimiter, boolean trim)
    506     {
    507         if (s == null)
    508         {
    509             return new ArrayList();
    510         }
    511
    512         List list = new ArrayList();
    513
    514         StringBuffer token = new StringBuffer();
    515         int begin = 0;
    516         boolean inEscape = false;
    517
    518         while (begin < s.length())
    519         {
    520             char c = s.charAt(begin);
    521             if (inEscape)
    522             {
    523                 // last character was the escape marker
    524                 // can current character be escaped?
    525                 if (c != delimiter && c != LIST_ESC_CHAR)
    526                 {
    527                     // no, also add escape character
    528                     token.append(LIST_ESC_CHAR);
    529                 }
    530                 token.append(c);
    531                 inEscape = false;
    532             }
    533
    534             else
    535             {
    536                 if (c == delimiter)
    537                 {
    538                     // found a list delimiter -> add token and reset buffer
    539                     String t = token.toString();
    540                     if (trim)
    541                     {
    542                         t = t.trim();
    543                     }
    544                     list.add(t);
    545                     token = new StringBuffer();
    546                 }
    547                 else if (c == LIST_ESC_CHAR)
    548                 {
    549                     // eventually escape next character
    550                     inEscape = true;
    551                 }
    552                 else
    553                 {
    554                     token.append(c);
    555                 }
    556             }
    557
    558             begin++;
    559         }
    560
    561         // Trailing delimiter?
    562         if (inEscape)
    563         {
    564             token.append(LIST_ESC_CHAR);
    565         }
    566         // Add last token
    567         String t = token.toString();
    568         if (trim)
    569         {
    570             t = t.trim();
    571         }
    572         list.add(t);
    573
    574         return list;
    575     }
    576
    577     /***
    578      * Split a string on the specified delimiter always trimming the elements.
    579      * This is a shortcut for <code>split(s, delimiter, true)</code>.
    580      *
    581      * @param s          the string to split
    582      * @param delimiter  the delimiter
    583      * @return a list with the single tokens
    584      */
    585     public static List split(String s, char delimiter)
    586     {
    587         return split(s, delimiter, true);
    588     }
    589
    590     /***
    591      * Escapes the delimiters that might be contained in the given string. This
    592      * method ensures that list delimiter characters that are part of a
    593      * property's value are correctly escaped when a configuration is saved to a
    594      * file. Otherwise when loaded again the property will be treated as a list
    595      * property. A single backslash will also be escaped.
    596      *
    597      * @param s the string with the value
    598      * @param delimiter the list delimiter to use
    599      * @return the correctly esaped string
    600      */
    601     public static String escapeDelimiters(String s, char delimiter)
    602     {
    603         String s1 = StringUtils.replace(s, LIST_ESCAPE, LIST_ESCAPE + LIST_ESCAPE);
    604         return StringUtils.replace(s1, String.valueOf(delimiter), LIST_ESCAPE + delimiter);
    605     }
    606
    607     /***
    608      * Convert the specified object into a Color. If the value is a String,
    609      * the format allowed is (#)?[0-9A-F]{6}([0-9A-F]{2})?. Examples:
    610      * <ul>
    611      *   <li>FF0000 (red)</li>
    612      *   <li>0000FFA0 (semi transparent blue)</li>
    613      *   <li>#CCCCCC (gray)</li>
    614      *   <li>#00FF00A0 (semi transparent green)</li>
    615      * </ul>
    616      *
    617      * @param value the value to convert
    618      * @return the converted value
    619      * @throws ConversionException thrown if the value cannot be converted to a Color
    620      */
    621     public static Color toColor(Object value) throws ConversionException
    622     {
    623         if (value instanceof Color)
    624         {
    625             return (Color) value;
    626         }
    627         else if (value instanceof String && !StringUtils.isBlank((String) value))
    628         {
    629             String color = ((String) value).trim();
    630
    631             int[] components = new int[3];
    632
    633             // check the size of the string
    634             int minlength = components.length * 2;
    635             if (color.length() < minlength)
    636             {
    637                 throw new ConversionException("The value " + value + " can't be converted to a Color");
    638             }
    639
    640             // remove the leading #
    641             if (color.startsWith("#"))
    642             {
    643                 color = color.substring(1);
    644             }
    645
    646             try
    647             {
    648                 // parse the components
    649                 for (int i = 0; i < components.length; i++)
    650                 {
    651                     components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX);
    652                 }
    653
    654                 // parse the transparency
    655                 int alpha;
    656                 if (color.length() >= minlength + 2)
    657                 {
    658                     alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX);
    659                 }
    660                 else
    661                 {
    662                     alpha = Color.black.getAlpha();
    663                 }
    664
    665                 return new Color(components[0], components[1], components[2], alpha);
    666             }
    667             catch (Exception e)
    668             {
    669                 throw new ConversionException("The value " + value + " can't be converted to a Color", e);
    670             }
    671         }
    672         else
    673         {
    674             throw new ConversionException("The value " + value + " can't be converted to a Color");
    675         }
    676     }
    677
    678     /***
    679      * Convert the specified value into an internet address.
    680      *
    681      * @param value the value to convert
    682      * @return the converted value
    683      * @throws ConversionException thrown if the value cannot be converted to a InetAddress
    684      *
    685      * @since 1.5
    686      */
    687     static InetAddress toInetAddress(Object value) throws ConversionException
    688     {
    689         if (value instanceof InetAddress)
    690         {
    691             return (InetAddress) value;
    692         }
    693         else if (value instanceof String)
    694         {
    695             try
    696             {
    697                 return InetAddress.getByName((String) value);
    698             }
    699             catch (UnknownHostException e)
    700             {
    701                 throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e);
    702             }
    703         }
    704         else
    705         {
    706             throw new ConversionException("The value " + value + " can't be converted to a InetAddress");
    707         }
    708     }
    709
    710     /***
    711      * Convert the specified value into an email address.
    712      *
    713      * @param value the value to convert
    714      * @return the converted value
    715      * @throws ConversionException thrown if the value cannot be converted to an email address
    716      *
    717      * @since 1.5
    718      */
    719     static Object toInternetAddress(Object value) throws ConversionException
    720     {
    721         if (value.getClass().getName().equals(INTERNET_ADDRESS_CLASSNAME))
    722         {
    723             return value;
    724         }
    725         else if (value instanceof String)
    726         {
    727             try
    728             {
    729                 Constructor ctor = Class.forName(INTERNET_ADDRESS_CLASSNAME).getConstructor(new Class[] {String.class});
    730                 return ctor.newInstance(new Object[] {value});
    731             }
    732             catch (Exception e)
    733             {
    734                 throw new ConversionException("The value " + value + " can't be converted to a InternetAddress", e);
    735             }
    736         }
    737         else
    738         {
    739             throw new ConversionException("The value " + value + " can't be converted to a InternetAddress");
    740         }
    741     }
    742
    743     /***
    744      * Calls Class.isEnum() on Java 5, returns false on older JRE.
    745      */
    746     static boolean isEnum(Class cls)
    747     {
    748         if (!SystemUtils.isJavaVersionAtLeast(JAVA_VERSION_1_5))
    749         {
    750             return false;
    751         }
    752
    753         try
    754         {
    755             Method isEnumMethod = Class.class.getMethod("isEnum", new Class[] {});
    756             return ((Boolean) isEnumMethod.invoke(cls, new Object[] {})).booleanValue();
    757         }
    758         catch (Exception e)
    759         {
    760             // impossible
    761             throw new RuntimeException(e.getMessage());
    762         }
    763     }
    764
    765     /***
    766      * Convert the specified value into a Java 5 enum.
    767      *
    768      * @param value the value to convert
    769      * @param cls   the type of the enumeration
    770      * @return the converted value
    771      * @throws ConversionException thrown if the value cannot be converted to an enumeration
    772      *
    773      * @since 1.5
    774      */
    775     static Object toEnum(Object value, Class cls) throws ConversionException
    776     {
    777         if (value.getClass().equals(cls))
    778         {
    779             return value;
    780         }
    781         else if (value instanceof String)
    782         {
    783             try
    784             {
    785                 Method valueOfMethod = cls.getMethod("valueOf", new Class[] {String.class});
    786                 return valueOfMethod.invoke(null, new Object[] {value});
    787             }
    788             catch (Exception e)
    789             {
    790                 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
    791             }
    792         }
    793         else if (value instanceof Number)
    794         {
    795             try
    796             {
    797                 Method valuesMethod = cls.getMethod("values", new Class[] {});
    798                 Object valuesArray = valuesMethod.invoke(null, new Object[] {});
    799
    800                 return Array.get(valuesArray, ((Number) value).intValue());
    801             }
    802             catch (Exception e)
    803             {
    804                 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
    805             }
    806         }
    807         else
    808         {
    809             throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
    810         }
    811     }
    812
    813     /***
    814      * Convert the specified object into a Date.
    815      *
    816      * @param value  the value to convert
    817      * @param format the DateFormat pattern to parse String values
    818      * @return the converted value
    819      * @throws ConversionException thrown if the value cannot be converted to a Calendar
    820      */
    821     public static Date toDate(Object value, String format) throws ConversionException
    822     {
    823         if (value instanceof Date)
    824         {
    825             return (Date) value;
    826         }
    827         else if (value instanceof Calendar)
    828         {
    829             return ((Calendar) value).getTime();
    830         }
    831         else if (value instanceof String)
    832         {
    833             try
    834             {
    835                 return new SimpleDateFormat(format).parse((String) value);
    836             }
    837             catch (ParseException e)
    838             {
    839                 throw new ConversionException("The value " + value + " can't be converted to a Date", e);
    840             }
    841         }
    842         else
    843         {
    844             throw new ConversionException("The value " + value + " can't be converted to a Date");
    845         }
    846     }
    847
    848     /***
    849      * Convert the specified object into a Calendar.
    850      *
    851      * @param value  the value to convert
    852      * @param format the DateFormat pattern to parse String values
    853      * @return the converted value
    854      * @throws ConversionException thrown if the value cannot be converted to a Calendar
    855      */
    856     public static Calendar toCalendar(Object value, String format) throws ConversionException
    857     {
    858         if (value instanceof Calendar)
    859         {
    860             return (Calendar) value;
    861         }
    862         else if (value instanceof Date)
    863         {
    864             Calendar calendar = Calendar.getInstance();
    865             calendar.setTime((Date) value);
    866             return calendar;
    867         }
    868         else if (value instanceof String)
    869         {
    870             try
    871             {
    872                 Calendar calendar = Calendar.getInstance();
    873                 calendar.setTime(new SimpleDateFormat(format).parse((String) value));
    874                 return calendar;
    875             }
    876             catch (ParseException e)
    877             {
    878                 throw new ConversionException("The value " + value + " can't be converted to a Calendar", e);
    879             }
    880         }
    881         else
    882         {
    883             throw new ConversionException("The value " + value + " can't be converted to a Calendar");
    884         }
    885     }
    886
    887     /***
    888      * Return an iterator over the simple values of a composite value. The value
    889      * specified is handled depending on its type:
    890      * <ul>
    891      *   <li>Strings are checked for delimiter characters and splitted if necessary.</li>
    892      *   <li>For collections the single elements are checked.</li>
    893      *   <li>Arrays are treated like collections.</li>
    894      *   <li>All other types are directly inserted.</li>
    895      *   <li>Recursive combinations are supported, e.g. a collection containing array that contain strings.</li>
    896      * </ul>
    897      *
    898      * @param value     the value to "split"
    899      * @param delimiter the delimiter for String values
    900      * @return an iterator for accessing the single values
    901      */
    902     public static Iterator toIterator(Object value, char delimiter)
    903     {
    904         if (value == null)
    905         {
    906             return IteratorUtils.emptyIterator();
    907         }
    908         if (value instanceof String)
    909         {
    910             String s = (String) value;
    911             if (s.indexOf(delimiter) > 0)
    912             {
    913                 return split((String) value, delimiter).iterator();
    914             }
    915             else
    916             {
    917                 return new SingletonIterator(value);
    918             }
    919         }
    920         else if (value instanceof Collection)
    921         {
    922             return toIterator(((Collection) value).iterator(), delimiter);
    923         }
    924         else if (value.getClass().isArray())
    925         {
    926             return toIterator(IteratorUtils.arrayIterator(value), delimiter);
    927         }
    928         else if (value instanceof Iterator)
    929         {
    930             Iterator iterator = (Iterator) value;
    931             IteratorChain chain = new IteratorChain();
    932             while (iterator.hasNext())
    933             {
    934                 chain.addIterator(toIterator(iterator.next(), delimiter));
    935             }
    936             return chain;
    937         }
    938         else
    939         {
    940             return new SingletonIterator(value);
    941         }
    942     }
    943
    944     /***
    945      * Performs interpolation of the specified value. This method checks if the
    946      * given value contains variables of the form <code>${...}</code>. If
    947      * this is the case, all occurrances will be substituted by their current
    948      * values.
    949      *
    950      * @param value the value to be interpolated
    951      * @param config the current configuration object
    952      * @return the interpolated value
    953      */
    954     public static Object interpolate(Object value, AbstractConfiguration config)
    955     {
    956         if (value instanceof String)
    957         {
    958             return config.getSubstitutor().replace((String) value);
    959         }
    960         else
    961         {
    962             return value;
    963         }
    964     }
    965 }