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: 589380 $, $Date: 2007-10-28 17:37:35 +0100 (So, 28 Okt 2007) $
    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      * @return a list with the single tokens
    503      */
    504     public static List split(String s, char delimiter)
    505     {
    506         if (s == null)
    507         {
    508             return new ArrayList();
    509         }
    510
    511         List list = new ArrayList();
    512
    513         StringBuffer token = new StringBuffer();
    514         int begin = 0;
    515         boolean inEscape = false;
    516
    517         while (begin < s.length())
    518         {
    519             char c = s.charAt(begin);
    520             if (inEscape)
    521             {
    522                 // last character was the escape marker
    523                 // can current character be escaped?
    524                 if (c != delimiter && c != LIST_ESC_CHAR)
    525                 {
    526                     // no, also add escape character
    527                     token.append(LIST_ESC_CHAR);
    528                 }
    529                 token.append(c);
    530                 inEscape = false;
    531             }
    532
    533             else
    534             {
    535                 if (c == delimiter)
    536                 {
    537                     // found a list delimiter -> add token and reset buffer
    538                     list.add(token.toString().trim());
    539                     token = new StringBuffer();
    540                 }
    541                 else if (c == LIST_ESC_CHAR)
    542                 {
    543                     // eventually escape next character
    544                     inEscape = true;
    545                 }
    546                 else
    547                 {
    548                     token.append(c);
    549                 }
    550             }
    551
    552             begin++;
    553         }
    554
    555         // Trailing delimiter?
    556         if (inEscape)
    557         {
    558             token.append(LIST_ESC_CHAR);
    559         }
    560         // Add last token
    561         list.add(token.toString().trim());
    562
    563         return list;
    564     }
    565
    566     /***
    567      * Escapes the delimiters that might be contained in the given string. This
    568      * method ensures that list delimiter characters that are part of a
    569      * property's value are correctly escaped when a configuration is saved to a
    570      * file. Otherwise when loaded again the property will be treated as a list
    571      * property. A single backslash will also be escaped.
    572      *
    573      * @param s the string with the value
    574      * @param delimiter the list delimiter to use
    575      * @return the correctly esaped string
    576      */
    577     public static String escapeDelimiters(String s, char delimiter)
    578     {
    579         String s1 = StringUtils.replace(s, LIST_ESCAPE, LIST_ESCAPE + LIST_ESCAPE);
    580         return StringUtils.replace(s1, String.valueOf(delimiter), LIST_ESCAPE + delimiter);
    581     }
    582
    583     /***
    584      * Convert the specified object into a Color. If the value is a String,
    585      * the format allowed is (#)?[0-9A-F]{6}([0-9A-F]{2})?. Examples:
    586      * <ul>
    587      *   <li>FF0000 (red)</li>
    588      *   <li>0000FFA0 (semi transparent blue)</li>
    589      *   <li>#CCCCCC (gray)</li>
    590      *   <li>#00FF00A0 (semi transparent green)</li>
    591      * </ul>
    592      *
    593      * @param value the value to convert
    594      * @return the converted value
    595      * @throws ConversionException thrown if the value cannot be converted to a Color
    596      */
    597     public static Color toColor(Object value) throws ConversionException
    598     {
    599         if (value instanceof Color)
    600         {
    601             return (Color) value;
    602         }
    603         else if (value instanceof String && !StringUtils.isBlank((String) value))
    604         {
    605             String color = ((String) value).trim();
    606
    607             int[] components = new int[3];
    608
    609             // check the size of the string
    610             int minlength = components.length * 2;
    611             if (color.length() < minlength)
    612             {
    613                 throw new ConversionException("The value " + value + " can't be converted to a Color");
    614             }
    615
    616             // remove the leading #
    617             if (color.startsWith("#"))
    618             {
    619                 color = color.substring(1);
    620             }
    621
    622             try
    623             {
    624                 // parse the components
    625                 for (int i = 0; i < components.length; i++)
    626                 {
    627                     components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX);
    628                 }
    629
    630                 // parse the transparency
    631                 int alpha;
    632                 if (color.length() >= minlength + 2)
    633                 {
    634                     alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX);
    635                 }
    636                 else
    637                 {
    638                     alpha = Color.black.getAlpha();
    639                 }
    640
    641                 return new Color(components[0], components[1], components[2], alpha);
    642             }
    643             catch (Exception e)
    644             {
    645                 throw new ConversionException("The value " + value + " can't be converted to a Color", e);
    646             }
    647         }
    648         else
    649         {
    650             throw new ConversionException("The value " + value + " can't be converted to a Color");
    651         }
    652     }
    653
    654     /***
    655      * Convert the specified value into an internet address.
    656      *
    657      * @param value the value to convert
    658      * @return the converted value
    659      * @throws ConversionException thrown if the value cannot be converted to a InetAddress
    660      *
    661      * @since 1.5
    662      */
    663     static InetAddress toInetAddress(Object value) throws ConversionException
    664     {
    665         if (value instanceof InetAddress)
    666         {
    667             return (InetAddress) value;
    668         }
    669         else if (value instanceof String)
    670         {
    671             try
    672             {
    673                 return InetAddress.getByName((String) value);
    674             }
    675             catch (UnknownHostException e)
    676             {
    677                 throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e);
    678             }
    679         }
    680         else
    681         {
    682             throw new ConversionException("The value " + value + " can't be converted to a InetAddress");
    683         }
    684     }
    685
    686     /***
    687      * Convert the specified value into an email address.
    688      *
    689      * @param value the value to convert
    690      * @return the converted value
    691      * @throws ConversionException thrown if the value cannot be converted to an email address
    692      *
    693      * @since 1.5
    694      */
    695     static Object toInternetAddress(Object value) throws ConversionException
    696     {
    697         if (value.getClass().getName().equals(INTERNET_ADDRESS_CLASSNAME))
    698         {
    699             return value;
    700         }
    701         else if (value instanceof String)
    702         {
    703             try
    704             {
    705                 Constructor ctor = Class.forName(INTERNET_ADDRESS_CLASSNAME).getConstructor(new Class[] {String.class});
    706                 return ctor.newInstance(new Object[] {value});
    707             }
    708             catch (Exception e)
    709             {
    710                 throw new ConversionException("The value " + value + " can't be converted to a InternetAddress", e);
    711             }
    712         }
    713         else
    714         {
    715             throw new ConversionException("The value " + value + " can't be converted to a InternetAddress");
    716         }
    717     }
    718
    719     /***
    720      * Calls Class.isEnum() on Java 5, returns false on older JRE.
    721      */
    722     static boolean isEnum(Class cls)
    723     {
    724         if (!SystemUtils.isJavaVersionAtLeast(JAVA_VERSION_1_5))
    725         {
    726             return false;
    727         }
    728
    729         try
    730         {
    731             Method isEnumMethod = Class.class.getMethod("isEnum", new Class[] {});
    732             return ((Boolean) isEnumMethod.invoke(cls, new Object[] {})).booleanValue();
    733         }
    734         catch (Exception e)
    735         {
    736             // impossible
    737             throw new RuntimeException(e.getMessage());
    738         }
    739     }
    740
    741     /***
    742      * Convert the specified value into a Java 5 enum.
    743      *
    744      * @param value the value to convert
    745      * @param cls   the type of the enumeration
    746      * @return the converted value
    747      * @throws ConversionException thrown if the value cannot be converted to an enumeration
    748      *
    749      * @since 1.5
    750      */
    751     static Object toEnum(Object value, Class cls) throws ConversionException
    752     {
    753         if (value.getClass().equals(cls))
    754         {
    755             return value;
    756         }
    757         else if (value instanceof String)
    758         {
    759             try
    760             {
    761                 Method valueOfMethod = cls.getMethod("valueOf", new Class[] {String.class});
    762                 return valueOfMethod.invoke(null, new Object[] {value});
    763             }
    764             catch (Exception e)
    765             {
    766                 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
    767             }
    768         }
    769         else if (value instanceof Number)
    770         {
    771             try
    772             {
    773                 Method valuesMethod = cls.getMethod("values", new Class[] {});
    774                 Object valuesArray = valuesMethod.invoke(null, new Object[] {});
    775
    776                 return Array.get(valuesArray, ((Number) value).intValue());
    777             }
    778             catch (Exception e)
    779             {
    780                 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
    781             }
    782         }
    783         else
    784         {
    785             throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
    786         }
    787     }
    788
    789     /***
    790      * Convert the specified object into a Date.
    791      *
    792      * @param value  the value to convert
    793      * @param format the DateFormat pattern to parse String values
    794      * @return the converted value
    795      * @throws ConversionException thrown if the value cannot be converted to a Calendar
    796      */
    797     public static Date toDate(Object value, String format) throws ConversionException
    798     {
    799         if (value instanceof Date)
    800         {
    801             return (Date) value;
    802         }
    803         else if (value instanceof Calendar)
    804         {
    805             return ((Calendar) value).getTime();
    806         }
    807         else if (value instanceof String)
    808         {
    809             try
    810             {
    811                 return new SimpleDateFormat(format).parse((String) value);
    812             }
    813             catch (ParseException e)
    814             {
    815                 throw new ConversionException("The value " + value + " can't be converted to a Date", e);
    816             }
    817         }
    818         else
    819         {
    820             throw new ConversionException("The value " + value + " can't be converted to a Date");
    821         }
    822     }
    823
    824     /***
    825      * Convert the specified object into a Calendar.
    826      *
    827      * @param value  the value to convert
    828      * @param format the DateFormat pattern to parse String values
    829      * @return the converted value
    830      * @throws ConversionException thrown if the value cannot be converted to a Calendar
    831      */
    832     public static Calendar toCalendar(Object value, String format) throws ConversionException
    833     {
    834         if (value instanceof Calendar)
    835         {
    836             return (Calendar) value;
    837         }
    838         else if (value instanceof Date)
    839         {
    840             Calendar calendar = Calendar.getInstance();
    841             calendar.setTime((Date) value);
    842             return calendar;
    843         }
    844         else if (value instanceof String)
    845         {
    846             try
    847             {
    848                 Calendar calendar = Calendar.getInstance();
    849                 calendar.setTime(new SimpleDateFormat(format).parse((String) value));
    850                 return calendar;
    851             }
    852             catch (ParseException e)
    853             {
    854                 throw new ConversionException("The value " + value + " can't be converted to a Calendar", e);
    855             }
    856         }
    857         else
    858         {
    859             throw new ConversionException("The value " + value + " can't be converted to a Calendar");
    860         }
    861     }
    862
    863     /***
    864      * Return an iterator over the simple values of a composite value. The value
    865      * specified is handled depending on its type:
    866      * <ul>
    867      *   <li>Strings are checked for delimiter characters and splitted if necessary.</li>
    868      *   <li>For collections the single elements are checked.</li>
    869      *   <li>Arrays are treated like collections.</li>
    870      *   <li>All other types are directly inserted.</li>
    871      *   <li>Recursive combinations are supported, e.g. a collection containing array that contain strings.</li>
    872      * </ul>
    873      *
    874      * @param value     the value to "split"
    875      * @param delimiter the delimiter for String values
    876      * @return an iterator for accessing the single values
    877      */
    878     public static Iterator toIterator(Object value, char delimiter)
    879     {
    880         if (value == null)
    881         {
    882             return IteratorUtils.emptyIterator();
    883         }
    884         if (value instanceof String)
    885         {
    886             String s = (String) value;
    887             if (s.indexOf(delimiter) > 0)
    888             {
    889                 return split((String) value, delimiter).iterator();
    890             }
    891             else
    892             {
    893                 return new SingletonIterator(value);
    894             }
    895         }
    896         else if (value instanceof Collection)
    897         {
    898             return toIterator(((Collection) value).iterator(), delimiter);
    899         }
    900         else if (value.getClass().isArray())
    901         {
    902             return toIterator(IteratorUtils.arrayIterator(value), delimiter);
    903         }
    904         else if (value instanceof Iterator)
    905         {
    906             Iterator iterator = (Iterator) value;
    907             IteratorChain chain = new IteratorChain();
    908             while (iterator.hasNext())
    909             {
    910                 chain.addIterator(toIterator(iterator.next(), delimiter));
    911             }
    912             return chain;
    913         }
    914         else
    915         {
    916             return new SingletonIterator(value);
    917         }
    918     }
    919
    920     /***
    921      * Performs interpolation of the specified value. This method checks if the
    922      * given value contains variables of the form <code>${...}</code>. If
    923      * this is the case, all occurrances will be substituted by their current
    924      * values.
    925      *
    926      * @param value the value to be interpolated
    927      * @param config the current configuration object
    928      * @return the interpolated value
    929      */
    930     public static Object interpolate(Object value, AbstractConfiguration config)
    931     {
    932         if (value instanceof String)
    933         {
    934             return config.getSubstitutor().replace((String) value);
    935         }
    936         else
    937         {
    938             return value;
    939         }
    940     }
    941 }