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 }