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: 662269 $, $Date: 2008-06-01 21:30:02 +0200 (So, 01 Jun 2008) $ 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 reset 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 ensures that list delimiter characters that are part of a 593 * property's value are correctly escaped when a configuration is saved to a 594 * file. Otherwise when loaded again the property will be treated as a list 595 * property. A single backslash will also be escaped. 596 * 597 * @param s the string with the value 598 * @param delimiter the list delimiter to use 599 * @return the correctly esaped string 600 */ 601 public static String escapeDelimiters(String s, char delimiter) 602 { 603 String s1 = StringUtils.replace(s, LIST_ESCAPE, LIST_ESCAPE + LIST_ESCAPE); 604 return StringUtils.replace(s1, String.valueOf(delimiter), LIST_ESCAPE + delimiter); 605 } 606 607 /** 608 * Convert the specified object into a Color. If the value is a String, 609 * the format allowed is (#)?[0-9A-F]{6}([0-9A-F]{2})?. Examples: 610 * <ul> 611 * <li>FF0000 (red)</li> 612 * <li>0000FFA0 (semi transparent blue)</li> 613 * <li>#CCCCCC (gray)</li> 614 * <li>#00FF00A0 (semi transparent green)</li> 615 * </ul> 616 * 617 * @param value the value to convert 618 * @return the converted value 619 * @throws ConversionException thrown if the value cannot be converted to a Color 620 */ 621 public static Color toColor(Object value) throws ConversionException 622 { 623 if (value instanceof Color) 624 { 625 return (Color) value; 626 } 627 else if (value instanceof String && !StringUtils.isBlank((String) value)) 628 { 629 String color = ((String) value).trim(); 630 631 int[] components = new int[3]; 632 633 // check the size of the string 634 int minlength = components.length * 2; 635 if (color.length() < minlength) 636 { 637 throw new ConversionException("The value " + value + " can't be converted to a Color"); 638 } 639 640 // remove the leading # 641 if (color.startsWith("#")) 642 { 643 color = color.substring(1); 644 } 645 646 try 647 { 648 // parse the components 649 for (int i = 0; i < components.length; i++) 650 { 651 components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX); 652 } 653 654 // parse the transparency 655 int alpha; 656 if (color.length() >= minlength + 2) 657 { 658 alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX); 659 } 660 else 661 { 662 alpha = Color.black.getAlpha(); 663 } 664 665 return new Color(components[0], components[1], components[2], alpha); 666 } 667 catch (Exception e) 668 { 669 throw new ConversionException("The value " + value + " can't be converted to a Color", e); 670 } 671 } 672 else 673 { 674 throw new ConversionException("The value " + value + " can't be converted to a Color"); 675 } 676 } 677 678 /** 679 * Convert the specified value into an internet address. 680 * 681 * @param value the value to convert 682 * @return the converted value 683 * @throws ConversionException thrown if the value cannot be converted to a InetAddress 684 * 685 * @since 1.5 686 */ 687 static InetAddress toInetAddress(Object value) throws ConversionException 688 { 689 if (value instanceof InetAddress) 690 { 691 return (InetAddress) value; 692 } 693 else if (value instanceof String) 694 { 695 try 696 { 697 return InetAddress.getByName((String) value); 698 } 699 catch (UnknownHostException e) 700 { 701 throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e); 702 } 703 } 704 else 705 { 706 throw new ConversionException("The value " + value + " can't be converted to a InetAddress"); 707 } 708 } 709 710 /** 711 * Convert the specified value into an email address. 712 * 713 * @param value the value to convert 714 * @return the converted value 715 * @throws ConversionException thrown if the value cannot be converted to an email address 716 * 717 * @since 1.5 718 */ 719 static Object toInternetAddress(Object value) throws ConversionException 720 { 721 if (value.getClass().getName().equals(INTERNET_ADDRESS_CLASSNAME)) 722 { 723 return value; 724 } 725 else if (value instanceof String) 726 { 727 try 728 { 729 Constructor ctor = Class.forName(INTERNET_ADDRESS_CLASSNAME).getConstructor(new Class[] {String.class}); 730 return ctor.newInstance(new Object[] {value}); 731 } 732 catch (Exception e) 733 { 734 throw new ConversionException("The value " + value + " can't be converted to a InternetAddress", e); 735 } 736 } 737 else 738 { 739 throw new ConversionException("The value " + value + " can't be converted to a InternetAddress"); 740 } 741 } 742 743 /** 744 * Calls Class.isEnum() on Java 5, returns false on older JRE. 745 */ 746 static boolean isEnum(Class cls) 747 { 748 if (!SystemUtils.isJavaVersionAtLeast(JAVA_VERSION_1_5)) 749 { 750 return false; 751 } 752 753 try 754 { 755 Method isEnumMethod = Class.class.getMethod("isEnum", new Class[] {}); 756 return ((Boolean) isEnumMethod.invoke(cls, new Object[] {})).booleanValue(); 757 } 758 catch (Exception e) 759 { 760 // impossible 761 throw new RuntimeException(e.getMessage()); 762 } 763 } 764 765 /** 766 * Convert the specified value into a Java 5 enum. 767 * 768 * @param value the value to convert 769 * @param cls the type of the enumeration 770 * @return the converted value 771 * @throws ConversionException thrown if the value cannot be converted to an enumeration 772 * 773 * @since 1.5 774 */ 775 static Object toEnum(Object value, Class cls) throws ConversionException 776 { 777 if (value.getClass().equals(cls)) 778 { 779 return value; 780 } 781 else if (value instanceof String) 782 { 783 try 784 { 785 Method valueOfMethod = cls.getMethod("valueOf", new Class[] {String.class}); 786 return valueOfMethod.invoke(null, new Object[] {value}); 787 } 788 catch (Exception e) 789 { 790 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName()); 791 } 792 } 793 else if (value instanceof Number) 794 { 795 try 796 { 797 Method valuesMethod = cls.getMethod("values", new Class[] {}); 798 Object valuesArray = valuesMethod.invoke(null, new Object[] {}); 799 800 return Array.get(valuesArray, ((Number) value).intValue()); 801 } 802 catch (Exception e) 803 { 804 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName()); 805 } 806 } 807 else 808 { 809 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName()); 810 } 811 } 812 813 /** 814 * Convert the specified object into a Date. 815 * 816 * @param value the value to convert 817 * @param format the DateFormat pattern to parse String values 818 * @return the converted value 819 * @throws ConversionException thrown if the value cannot be converted to a Calendar 820 */ 821 public static Date toDate(Object value, String format) throws ConversionException 822 { 823 if (value instanceof Date) 824 { 825 return (Date) value; 826 } 827 else if (value instanceof Calendar) 828 { 829 return ((Calendar) value).getTime(); 830 } 831 else if (value instanceof String) 832 { 833 try 834 { 835 return new SimpleDateFormat(format).parse((String) value); 836 } 837 catch (ParseException e) 838 { 839 throw new ConversionException("The value " + value + " can't be converted to a Date", e); 840 } 841 } 842 else 843 { 844 throw new ConversionException("The value " + value + " can't be converted to a Date"); 845 } 846 } 847 848 /** 849 * Convert the specified object into a Calendar. 850 * 851 * @param value the value to convert 852 * @param format the DateFormat pattern to parse String values 853 * @return the converted value 854 * @throws ConversionException thrown if the value cannot be converted to a Calendar 855 */ 856 public static Calendar toCalendar(Object value, String format) throws ConversionException 857 { 858 if (value instanceof Calendar) 859 { 860 return (Calendar) value; 861 } 862 else if (value instanceof Date) 863 { 864 Calendar calendar = Calendar.getInstance(); 865 calendar.setTime((Date) value); 866 return calendar; 867 } 868 else if (value instanceof String) 869 { 870 try 871 { 872 Calendar calendar = Calendar.getInstance(); 873 calendar.setTime(new SimpleDateFormat(format).parse((String) value)); 874 return calendar; 875 } 876 catch (ParseException e) 877 { 878 throw new ConversionException("The value " + value + " can't be converted to a Calendar", e); 879 } 880 } 881 else 882 { 883 throw new ConversionException("The value " + value + " can't be converted to a Calendar"); 884 } 885 } 886 887 /** 888 * Return an iterator over the simple values of a composite value. The value 889 * specified is handled depending on its type: 890 * <ul> 891 * <li>Strings are checked for delimiter characters and splitted if necessary.</li> 892 * <li>For collections the single elements are checked.</li> 893 * <li>Arrays are treated like collections.</li> 894 * <li>All other types are directly inserted.</li> 895 * <li>Recursive combinations are supported, e.g. a collection containing array that contain strings.</li> 896 * </ul> 897 * 898 * @param value the value to "split" 899 * @param delimiter the delimiter for String values 900 * @return an iterator for accessing the single values 901 */ 902 public static Iterator toIterator(Object value, char delimiter) 903 { 904 if (value == null) 905 { 906 return IteratorUtils.emptyIterator(); 907 } 908 if (value instanceof String) 909 { 910 String s = (String) value; 911 if (s.indexOf(delimiter) > 0) 912 { 913 return split((String) value, delimiter).iterator(); 914 } 915 else 916 { 917 return new SingletonIterator(value); 918 } 919 } 920 else if (value instanceof Collection) 921 { 922 return toIterator(((Collection) value).iterator(), delimiter); 923 } 924 else if (value.getClass().isArray()) 925 { 926 return toIterator(IteratorUtils.arrayIterator(value), delimiter); 927 } 928 else if (value instanceof Iterator) 929 { 930 Iterator iterator = (Iterator) value; 931 IteratorChain chain = new IteratorChain(); 932 while (iterator.hasNext()) 933 { 934 chain.addIterator(toIterator(iterator.next(), delimiter)); 935 } 936 return chain; 937 } 938 else 939 { 940 return new SingletonIterator(value); 941 } 942 } 943 944 /** 945 * Performs interpolation of the specified value. This method checks if the 946 * given value contains variables of the form <code>${...}</code>. If 947 * this is the case, all occurrances will be substituted by their current 948 * values. 949 * 950 * @param value the value to be interpolated 951 * @param config the current configuration object 952 * @return the interpolated value 953 */ 954 public static Object interpolate(Object value, AbstractConfiguration config) 955 { 956 if (value instanceof String) 957 { 958 return config.getSubstitutor().replace((String) value); 959 } 960 else 961 { 962 return value; 963 } 964 } 965 }