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