001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.configuration; 019 020 import java.awt.Color; 021 import java.lang.reflect.Array; 022 import java.lang.reflect.Constructor; 023 import java.lang.reflect.InvocationTargetException; 024 import java.math.BigDecimal; 025 import java.math.BigInteger; 026 import java.net.InetAddress; 027 import java.net.MalformedURLException; 028 import java.net.URL; 029 import java.net.UnknownHostException; 030 import java.text.ParseException; 031 import java.text.SimpleDateFormat; 032 import java.util.ArrayList; 033 import java.util.Calendar; 034 import java.util.Collection; 035 import java.util.Date; 036 import java.util.Iterator; 037 import java.util.LinkedList; 038 import java.util.List; 039 import java.util.Locale; 040 041 import org.apache.commons.lang.BooleanUtils; 042 import org.apache.commons.lang.StringUtils; 043 044 /** 045 * A utility class to convert the configuration properties into any type. 046 * 047 * @author Emmanuel Bourg 048 * @version $Id: PropertyConverter.java 1301990 2012-03-17 20:10:46Z oheger $ 049 * @since 1.1 050 */ 051 public final class PropertyConverter 052 { 053 /** Constant for the list delimiter as char.*/ 054 static final char LIST_ESC_CHAR = '\\'; 055 056 /** Constant for the list delimiter escaping character as string.*/ 057 static final String LIST_ESCAPE = String.valueOf(LIST_ESC_CHAR); 058 059 /** Constant for the prefix of hex numbers.*/ 060 private static final String HEX_PREFIX = "0x"; 061 062 /** Constant for the radix of hex numbers.*/ 063 private static final int HEX_RADIX = 16; 064 065 /** Constant for the prefix of binary numbers.*/ 066 private static final String BIN_PREFIX = "0b"; 067 068 /** Constant for the radix of binary numbers.*/ 069 private static final int BIN_RADIX = 2; 070 071 /** Constant for the argument classes of the Number constructor that takes a String. */ 072 private static final Class<?>[] CONSTR_ARGS = {String.class}; 073 074 /** The fully qualified name of {@link javax.mail.internet.InternetAddress} */ 075 private static final String INTERNET_ADDRESS_CLASSNAME = "javax.mail.internet.InternetAddress"; 076 077 /** 078 * Private constructor prevents instances from being created. 079 */ 080 private PropertyConverter() 081 { 082 // to prevent instantiation... 083 } 084 085 /** 086 * Converts the specified value to the target class. If the class is a 087 * primitive type (Integer.TYPE, Boolean.TYPE, etc) the value returned 088 * will use the wrapper type (Integer.class, Boolean.class, etc). 089 * 090 * @param cls the target class of the converted value 091 * @param value the value to convert 092 * @param params optional parameters used for the conversion 093 * @return the converted value 094 * @throws ConversionException if the value is not compatible with the requested type 095 * 096 * @since 1.5 097 */ 098 static Object to(Class<?> cls, Object value, Object[] params) throws ConversionException 099 { 100 if (cls.isInstance(value)) 101 { 102 return value; // no conversion needed 103 } 104 105 if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) 106 { 107 return toBoolean(value); 108 } 109 else if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) 110 { 111 if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) 112 { 113 return toInteger(value); 114 } 115 else if (Long.class.equals(cls) || Long.TYPE.equals(cls)) 116 { 117 return toLong(value); 118 } 119 else if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) 120 { 121 return toByte(value); 122 } 123 else if (Short.class.equals(cls) || Short.TYPE.equals(cls)) 124 { 125 return toShort(value); 126 } 127 else if (Float.class.equals(cls) || Float.TYPE.equals(cls)) 128 { 129 return toFloat(value); 130 } 131 else if (Double.class.equals(cls) || Double.TYPE.equals(cls)) 132 { 133 return toDouble(value); 134 } 135 else if (BigInteger.class.equals(cls)) 136 { 137 return toBigInteger(value); 138 } 139 else if (BigDecimal.class.equals(cls)) 140 { 141 return toBigDecimal(value); 142 } 143 } 144 else if (Date.class.equals(cls)) 145 { 146 return toDate(value, (String) params[0]); 147 } 148 else if (Calendar.class.equals(cls)) 149 { 150 return toCalendar(value, (String) params[0]); 151 } 152 else if (URL.class.equals(cls)) 153 { 154 return toURL(value); 155 } 156 else if (Locale.class.equals(cls)) 157 { 158 return toLocale(value); 159 } 160 else if (isEnum(cls)) 161 { 162 return convertToEnum(cls, value); 163 } 164 else if (Color.class.equals(cls)) 165 { 166 return toColor(value); 167 } 168 else if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME)) 169 { 170 return toInternetAddress(value); 171 } 172 else if (InetAddress.class.isAssignableFrom(cls)) 173 { 174 return toInetAddress(value); 175 } 176 177 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 if (value instanceof Boolean) 197 { 198 return (Boolean) value; 199 } 200 else if (value instanceof String) 201 { 202 Boolean b = BooleanUtils.toBooleanObject((String) value); 203 if (b == null) 204 { 205 throw new ConversionException("The value " + value + " can't be converted to a Boolean object"); 206 } 207 return b; 208 } 209 else 210 { 211 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 Number n = toNumber(value, Byte.class); 225 if (n instanceof Byte) 226 { 227 return (Byte) n; 228 } 229 else 230 { 231 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 Number n = toNumber(value, Short.class); 245 if (n instanceof Short) 246 { 247 return (Short) n; 248 } 249 else 250 { 251 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 Number n = toNumber(value, Integer.class); 265 if (n instanceof Integer) 266 { 267 return (Integer) n; 268 } 269 else 270 { 271 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 Number n = toNumber(value, Long.class); 285 if (n instanceof Long) 286 { 287 return (Long) n; 288 } 289 else 290 { 291 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 Number n = toNumber(value, Float.class); 305 if (n instanceof Float) 306 { 307 return (Float) n; 308 } 309 else 310 { 311 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 Number n = toNumber(value, Double.class); 325 if (n instanceof Double) 326 { 327 return (Double) n; 328 } 329 else 330 { 331 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 Number n = toNumber(value, BigInteger.class); 345 if (n instanceof BigInteger) 346 { 347 return (BigInteger) n; 348 } 349 else 350 { 351 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 Number n = toNumber(value, BigDecimal.class); 365 if (n instanceof BigDecimal) 366 { 367 return (BigDecimal) n; 368 } 369 else 370 { 371 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 if (value instanceof Number) 390 { 391 return (Number) value; 392 } 393 else 394 { 395 String str = value.toString(); 396 if (str.startsWith(HEX_PREFIX)) 397 { 398 try 399 { 400 return new BigInteger(str.substring(HEX_PREFIX.length()), HEX_RADIX); 401 } 402 catch (NumberFormatException nex) 403 { 404 throw new ConversionException("Could not convert " + str 405 + " to " + targetClass.getName() 406 + "! Invalid hex number.", nex); 407 } 408 } 409 410 if (str.startsWith(BIN_PREFIX)) 411 { 412 try 413 { 414 return new BigInteger(str.substring(BIN_PREFIX.length()), BIN_RADIX); 415 } 416 catch (NumberFormatException nex) 417 { 418 throw new ConversionException("Could not convert " + str 419 + " to " + targetClass.getName() 420 + "! Invalid binary number.", nex); 421 } 422 } 423 424 try 425 { 426 Constructor<?> constr = targetClass.getConstructor(CONSTR_ARGS); 427 return (Number) constr.newInstance(new Object[]{str}); 428 } 429 catch (InvocationTargetException itex) 430 { 431 throw new ConversionException("Could not convert " + str 432 + " to " + targetClass.getName(), itex 433 .getTargetException()); 434 } 435 catch (Exception ex) 436 { 437 // Treat all possible exceptions the same way 438 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 if (value instanceof URL) 455 { 456 return (URL) value; 457 } 458 else if (value instanceof String) 459 { 460 try 461 { 462 return new URL((String) value); 463 } 464 catch (MalformedURLException e) 465 { 466 throw new ConversionException("The value " + value + " can't be converted to an URL", e); 467 } 468 } 469 else 470 { 471 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 if (value instanceof Locale) 485 { 486 return (Locale) value; 487 } 488 else if (value instanceof String) 489 { 490 List<String> elements = split((String) value, '_'); 491 int size = elements.size(); 492 493 if (size >= 1 && ((elements.get(0)).length() == 2 || (elements.get(0)).length() == 0)) 494 { 495 String language = elements.get(0); 496 String country = (size >= 2) ? elements.get(1) : ""; 497 String variant = (size >= 3) ? elements.get(2) : ""; 498 499 return new Locale(language, country, variant); 500 } 501 else 502 { 503 throw new ConversionException("The value " + value + " can't be converted to a Locale"); 504 } 505 } 506 else 507 { 508 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 if (s == null) 526 { 527 return new ArrayList<String>(); 528 } 529 530 List<String> list = new ArrayList<String>(); 531 532 StringBuilder token = new StringBuilder(); 533 int begin = 0; 534 boolean inEscape = false; 535 536 while (begin < s.length()) 537 { 538 char c = s.charAt(begin); 539 if (inEscape) 540 { 541 // last character was the escape marker 542 // can current character be escaped? 543 if (c != delimiter && c != LIST_ESC_CHAR) 544 { 545 // no, also add escape character 546 token.append(LIST_ESC_CHAR); 547 } 548 token.append(c); 549 inEscape = false; 550 } 551 552 else 553 { 554 if (c == delimiter) 555 { 556 // found a list delimiter -> add token and resetDefaultFileSystem buffer 557 String t = token.toString(); 558 if (trim) 559 { 560 t = t.trim(); 561 } 562 list.add(t); 563 token = new StringBuilder(); 564 } 565 else if (c == LIST_ESC_CHAR) 566 { 567 // eventually escape next character 568 inEscape = true; 569 } 570 else 571 { 572 token.append(c); 573 } 574 } 575 576 begin++; 577 } 578 579 // Trailing delimiter? 580 if (inEscape) 581 { 582 token.append(LIST_ESC_CHAR); 583 } 584 // Add last token 585 String t = token.toString(); 586 if (trim) 587 { 588 t = t.trim(); 589 } 590 list.add(t); 591 592 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 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 String s1 = StringUtils.replace(s, LIST_ESCAPE, LIST_ESCAPE + LIST_ESCAPE); 620 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 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 if (value instanceof Color) 658 { 659 return (Color) value; 660 } 661 else if (value instanceof String && !StringUtils.isBlank((String) value)) 662 { 663 String color = ((String) value).trim(); 664 665 int[] components = new int[3]; 666 667 // check the size of the string 668 int minlength = components.length * 2; 669 if (color.length() < minlength) 670 { 671 throw new ConversionException("The value " + value + " can't be converted to a Color"); 672 } 673 674 // remove the leading # 675 if (color.startsWith("#")) 676 { 677 color = color.substring(1); 678 } 679 680 try 681 { 682 // parse the components 683 for (int i = 0; i < components.length; i++) 684 { 685 components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX); 686 } 687 688 // parse the transparency 689 int alpha; 690 if (color.length() >= minlength + 2) 691 { 692 alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX); 693 } 694 else 695 { 696 alpha = Color.black.getAlpha(); 697 } 698 699 return new Color(components[0], components[1], components[2], alpha); 700 } 701 catch (Exception e) 702 { 703 throw new ConversionException("The value " + value + " can't be converted to a Color", e); 704 } 705 } 706 else 707 { 708 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 if (value instanceof InetAddress) 724 { 725 return (InetAddress) value; 726 } 727 else if (value instanceof String) 728 { 729 try 730 { 731 return InetAddress.getByName((String) value); 732 } 733 catch (UnknownHostException e) 734 { 735 throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e); 736 } 737 } 738 else 739 { 740 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 if (value.getClass().getName().equals(INTERNET_ADDRESS_CLASSNAME)) 756 { 757 return value; 758 } 759 else if (value instanceof String) 760 { 761 try 762 { 763 Constructor<?> ctor = Class.forName(INTERNET_ADDRESS_CLASSNAME) 764 .getConstructor(new Class[] {String.class}); 765 return ctor.newInstance(new Object[] {value}); 766 } 767 catch (Exception e) 768 { 769 throw new ConversionException("The value " + value + " can't be converted to a InternetAddress", e); 770 } 771 } 772 else 773 { 774 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 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 if (value.getClass().equals(cls)) 799 { 800 return cls.cast(value); 801 } 802 else if (value instanceof String) 803 { 804 try 805 { 806 return Enum.valueOf(cls, (String) value); 807 } 808 catch (Exception e) 809 { 810 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName()); 811 } 812 } 813 else if (value instanceof Number) 814 { 815 try 816 { 817 E[] enumConstants = cls.getEnumConstants(); 818 return enumConstants[((Number) value).intValue()]; 819 } 820 catch (Exception e) 821 { 822 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName()); 823 } 824 } 825 else 826 { 827 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 if (value instanceof Date) 842 { 843 return (Date) value; 844 } 845 else if (value instanceof Calendar) 846 { 847 return ((Calendar) value).getTime(); 848 } 849 else if (value instanceof String) 850 { 851 try 852 { 853 return new SimpleDateFormat(format).parse((String) value); 854 } 855 catch (ParseException e) 856 { 857 throw new ConversionException("The value " + value + " can't be converted to a Date", e); 858 } 859 } 860 else 861 { 862 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 if (value instanceof Calendar) 877 { 878 return (Calendar) value; 879 } 880 else if (value instanceof Date) 881 { 882 Calendar calendar = Calendar.getInstance(); 883 calendar.setTime((Date) value); 884 return calendar; 885 } 886 else if (value instanceof String) 887 { 888 try 889 { 890 Calendar calendar = Calendar.getInstance(); 891 calendar.setTime(new SimpleDateFormat(format).parse((String) value)); 892 return calendar; 893 } 894 catch (ParseException e) 895 { 896 throw new ConversionException("The value " + value + " can't be converted to a Calendar", e); 897 } 898 } 899 else 900 { 901 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 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 "flatten").</li> 935 * </ul> 936 * 937 * @param value the value to be processed 938 * @param delimiter the delimiter for String values 939 * @return a "flat" collection containing all primitive values of 940 * the passed in object 941 */ 942 private static Collection<?> flatten(Object value, char delimiter) 943 { 944 if (value instanceof String) 945 { 946 String s = (String) value; 947 if (s.indexOf(delimiter) > 0) 948 { 949 return split(s, delimiter); 950 } 951 } 952 953 Collection<Object> result = new LinkedList<Object>(); 954 if (value instanceof Iterable) 955 { 956 flattenIterator(result, ((Iterable<?>) value).iterator(), delimiter); 957 } 958 else if (value instanceof Iterator) 959 { 960 flattenIterator(result, (Iterator<?>) value, delimiter); 961 } 962 else if (value != null) 963 { 964 if (value.getClass().isArray()) 965 { 966 for (int len = Array.getLength(value), idx = 0; idx < len; idx++) 967 { 968 result.addAll(flatten(Array.get(value, idx), delimiter)); 969 } 970 } 971 else 972 { 973 result.add(value); 974 } 975 } 976 977 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 while (it.hasNext()) 991 { 992 target.addAll(flatten(it.next(), delimiter)); 993 } 994 } 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 if (value instanceof String) 1009 { 1010 return config.getSubstitutor().replace((String) value); 1011 } 1012 else 1013 { 1014 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 return toEnum(value, enumClass.asSubclass(Enum.class)); 1031 } 1032 }