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