1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.lang.reflect.Method;
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.net.InetAddress;
28 import java.net.MalformedURLException;
29 import java.net.URL;
30 import java.net.UnknownHostException;
31 import java.text.ParseException;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.Collection;
36 import java.util.Date;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Locale;
40
41 import org.apache.commons.collections.IteratorUtils;
42 import org.apache.commons.collections.iterators.IteratorChain;
43 import org.apache.commons.collections.iterators.SingletonIterator;
44 import org.apache.commons.lang.BooleanUtils;
45 import org.apache.commons.lang.StringUtils;
46 import org.apache.commons.lang.SystemUtils;
47
48 /***
49 * A utility class to convert the configuration properties into any type.
50 *
51 * @author Emmanuel Bourg
52 * @version $Revision: 662269 $, $Date: 2008-06-01 21:30:02 +0200 (So, 01 Jun 2008) $
53 * @since 1.1
54 */
55 public final class PropertyConverter
56 {
57 /*** Constant for the list delimiter as char.*/
58 static final char LIST_ESC_CHAR = '//';
59
60 /*** Constant for the list delimiter escaping character as string.*/
61 static final String LIST_ESCAPE = String.valueOf(LIST_ESC_CHAR);
62
63 /*** Constant for the prefix of hex numbers.*/
64 private static final String HEX_PREFIX = "0x";
65
66 /*** Constant for the radix of hex numbers.*/
67 private static final int HEX_RADIX = 16;
68
69 /*** Constant for the Java version 1.5.*/
70 private static final float JAVA_VERSION_1_5 = 1.5f;
71
72 /*** Constant for the argument classes of the Number constructor that takes a String. */
73 private static final Class[] CONSTR_ARGS = {String.class};
74
75 /*** The fully qualified name of {@link javax.mail.internet.InternetAddress} */
76 private static final String INTERNET_ADDRESS_CLASSNAME = "javax.mail.internet.InternetAddress";
77
78 /***
79 * Private constructor prevents instances from being created.
80 */
81 private PropertyConverter()
82 {
83
84 }
85
86 /***
87 * Converts the specified value to the target class. If the class is a
88 * primitive type (Integer.TYPE, Boolean.TYPE, etc) the value returned
89 * will use the wrapper type (Integer.class, Boolean.class, etc).
90 *
91 * @param cls the target class of the converted value
92 * @param value the value to convert
93 * @param params optional parameters used for the conversion
94 * @return the converted value
95 * @throws ConversionException if the value is not compatible with the requested type
96 *
97 * @since 1.5
98 */
99 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
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
524
525 if (c != delimiter && c != LIST_ESC_CHAR)
526 {
527
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
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
550 inEscape = true;
551 }
552 else
553 {
554 token.append(c);
555 }
556 }
557
558 begin++;
559 }
560
561
562 if (inEscape)
563 {
564 token.append(LIST_ESC_CHAR);
565 }
566
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
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
641 if (color.startsWith("#"))
642 {
643 color = color.substring(1);
644 }
645
646 try
647 {
648
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
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
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 }