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