View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration2.convert;
19  
20  import java.awt.Color;
21  import java.io.File;
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.InvocationTargetException;
24  import java.math.BigDecimal;
25  import java.math.BigInteger;
26  import java.net.InetAddress;
27  import java.net.MalformedURLException;
28  import java.net.URI;
29  import java.net.URISyntaxException;
30  import java.net.URL;
31  import java.net.UnknownHostException;
32  import java.nio.file.Path;
33  import java.nio.file.Paths;
34  import java.text.ParseException;
35  import java.text.SimpleDateFormat;
36  import java.time.Duration;
37  import java.time.format.DateTimeParseException;
38  import java.util.Calendar;
39  import java.util.Date;
40  import java.util.Locale;
41  import java.util.regex.Pattern;
42  import java.util.regex.PatternSyntaxException;
43  
44  import org.apache.commons.configuration2.ex.ConversionException;
45  import org.apache.commons.lang3.BooleanUtils;
46  import org.apache.commons.lang3.StringUtils;
47  
48  /**
49   * A utility class to convert the configuration properties into any type.
50   *
51   * @since 2.8.0
52   */
53  public final class PropertyConverter {
54  
55      /** Constant for the prefix of hex numbers. */
56      private static final String HEX_PREFIX = "0x";
57  
58      /** Constant for the radix of hex numbers. */
59      private static final int HEX_RADIX = 16;
60  
61      /** Constant for the prefix of binary numbers. */
62      private static final String BIN_PREFIX = "0b";
63  
64      /** Constant for the radix of binary numbers. */
65      private static final int BIN_RADIX = 2;
66  
67      /** Constant for the argument classes of the Number constructor that takes a String. */
68      private static final Class<?>[] CONSTR_ARGS = {String.class};
69  
70      /** The fully qualified name of {@code javax.mail.internet.InternetAddress}, as used in the javamail-1.* API.  */
71      private static final String INTERNET_ADDRESS_CLASSNAME_JAVAX = "javax.mail.internet.InternetAddress";
72  
73      /** The fully qualified name of {@code jakarta.mail.internet.InternetAddress}, as used in the javamail-2.0+ API. */
74      private static final String INTERNET_ADDRESS_CLASSNAME_JAKARTA = "jakarta.mail.internet.InternetAddress";
75  
76      /**
77       * Private constructor prevents instances from being created.
78       */
79      private PropertyConverter() {
80          // presvents instantiation.
81      }
82  
83      /**
84       * Converts the specified value object to the given target data class. If additional
85       * information is required for this conversion, it is obtained from the passed in {@code DefaultConversionHandler}
86       * object. If the class is a primitive type (Integer.TYPE, Boolean.TYPE, etc), the value returned will use the wrapper
87       * type (Integer.class, Boolean.class, etc).
88       *
89       * @param cls the target class of the converted value
90       * @param value the value to convert
91       * @param convHandler the conversion handler object
92       * @return the converted value
93       * @throws ConversionException if the value is not compatible with the requested type
94       */
95      public static Object to(final Class<?> cls, final Object value, final DefaultConversionHandler convHandler) throws ConversionException {
96          if (cls.isInstance(value)) {
97              return value; // no conversion needed
98          }
99  
100         if (String.class.equals(cls)) {
101             return String.valueOf(value);
102         }
103         if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) {
104             return toBoolean(value);
105         }
106         if (Character.class.equals(cls) || Character.TYPE.equals(cls)) {
107             return toCharacter(value);
108         }
109         if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) {
110             if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) {
111                 return toInteger(value);
112             }
113             if (Long.class.equals(cls) || Long.TYPE.equals(cls)) {
114                 return toLong(value);
115             }
116             if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) {
117                 return toByte(value);
118             }
119             if (Short.class.equals(cls) || Short.TYPE.equals(cls)) {
120                 return toShort(value);
121             }
122             if (Float.class.equals(cls) || Float.TYPE.equals(cls)) {
123                 return toFloat(value);
124             }
125             if (Double.class.equals(cls) || Double.TYPE.equals(cls)) {
126                 return toDouble(value);
127             }
128             if (BigInteger.class.equals(cls)) {
129                 return toBigInteger(value);
130             }
131             if (BigDecimal.class.equals(cls)) {
132                 return toBigDecimal(value);
133             }
134         } else if (Date.class.equals(cls)) {
135             return toDate(value, convHandler.getDateFormat());
136         } else if (Calendar.class.equals(cls)) {
137             return toCalendar(value, convHandler.getDateFormat());
138         } else if (File.class.equals(cls)) {
139             return toFile(value);
140         } else if (Path.class.equals(cls)) {
141             return toPath(value);
142         } else if (URI.class.equals(cls)) {
143             return toURI(value);
144         } else if (URL.class.equals(cls)) {
145             return toURL(value);
146         } else if (Pattern.class.equals(cls)) {
147             return toPattern(value);
148         } else if (Locale.class.equals(cls)) {
149             return toLocale(value);
150         } else if (cls.isEnum()) {
151             return convertToEnum(cls, value);
152         } else if (Color.class.equals(cls)) {
153             return toColor(value);
154         } else if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAVAX)) {
155             // javamail-1.* With javax.mail.* namespace.
156             return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAVAX);
157         } else if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAKARTA)) {
158             // javamail-2.0+, with jakarta.mail.* namespace.
159             return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAKARTA);
160         } else if (InetAddress.class.isAssignableFrom(cls)) {
161             return toInetAddress(value);
162         } else if (Duration.class.equals(cls)) {
163             return toDuration(value);
164         }
165 
166         throw new ConversionException("The value '" + value + "' (" + value.getClass() + ")" + " can't be converted to a " + cls.getName() + " object");
167     }
168 
169     /**
170      * Converts the specified object into a Boolean. Internally the {@code org.apache.commons.lang.BooleanUtils} class from
171      * the <a href="https://commons.apache.org/lang/">Commons Lang</a> project is used to perform this conversion. This
172      * class accepts some more tokens for the boolean value of <b>true</b>, e.g. {@code yes} and {@code on}. Please refer to
173      * the documentation of this class for more details.
174      *
175      * @param value the value to convert
176      * @return the converted value
177      * @throws ConversionException thrown if the value cannot be converted to a boolean
178      */
179     public static Boolean toBoolean(final Object value) throws ConversionException {
180         if (value instanceof Boolean) {
181             return (Boolean) value;
182         }
183         if (!(value instanceof String)) {
184             throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
185         }
186         final Boolean b = BooleanUtils.toBooleanObject((String) value);
187         if (b == null) {
188             throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
189         }
190         return b;
191     }
192 
193     /**
194      * Converts the specified value object to a {@code Character}. This method converts the passed in object to a string. If
195      * the string has exactly one character, this character is returned as result. Otherwise, conversion fails.
196      *
197      * @param value the value to be converted
198      * @return the resulting {@code Character} object
199      * @throws ConversionException if the conversion is not possible
200      */
201     public static Character toCharacter(final Object value) throws ConversionException {
202         final String strValue = String.valueOf(value);
203         if (strValue.length() == 1) {
204             return Character.valueOf(strValue.charAt(0));
205         }
206         throw new ConversionException(String.format("The value '%s' cannot be converted to a Character object!", strValue));
207     }
208 
209     /**
210      * Converts the specified object into a Byte.
211      *
212      * @param value the value to convert
213      * @return the converted value
214      * @throws ConversionException thrown if the value cannot be converted to a byte
215      */
216     public static Byte toByte(final Object value) throws ConversionException {
217         final Number n = toNumber(value, Byte.class);
218         if (n instanceof Byte) {
219             return (Byte) n;
220         }
221         return n.byteValue();
222     }
223 
224     /**
225      * Converts the specified object into a Short.
226      *
227      * @param value the value to convert
228      * @return the converted value
229      * @throws ConversionException thrown if the value cannot be converted to a short
230      */
231     public static Short toShort(final Object value) throws ConversionException {
232         final Number n = toNumber(value, Short.class);
233         if (n instanceof Short) {
234             return (Short) n;
235         }
236         return n.shortValue();
237     }
238 
239     /**
240      * Converts the specified object into an Integer.
241      *
242      * @param value the value to convert
243      * @return the converted value
244      * @throws ConversionException thrown if the value cannot be converted to an integer
245      */
246     public static Integer toInteger(final Object value) throws ConversionException {
247         final Number n = toNumber(value, Integer.class);
248         if (n instanceof Integer) {
249             return (Integer) n;
250         }
251         return n.intValue();
252     }
253 
254     /**
255      * Converts the specified object into a Long.
256      *
257      * @param value the value to convert
258      * @return the converted value
259      * @throws ConversionException thrown if the value cannot be converted to a Long
260      */
261     public static Long toLong(final Object value) throws ConversionException {
262         final Number n = toNumber(value, Long.class);
263         if (n instanceof Long) {
264             return (Long) n;
265         }
266         return n.longValue();
267     }
268 
269     /**
270      * Converts the specified object into a Float.
271      *
272      * @param value the value to convert
273      * @return the converted value
274      * @throws ConversionException thrown if the value cannot be converted to a Float
275      */
276     public static Float toFloat(final Object value) throws ConversionException {
277         final Number n = toNumber(value, Float.class);
278         if (n instanceof Float) {
279             return (Float) n;
280         }
281         return Float.valueOf(n.floatValue());
282     }
283 
284     /**
285      * Converts the specified object into a Double.
286      *
287      * @param value the value to convert
288      * @return the converted value
289      * @throws ConversionException thrown if the value cannot be converted to a Double
290      */
291     public static Double toDouble(final Object value) throws ConversionException {
292         final Number n = toNumber(value, Double.class);
293         if (n instanceof Double) {
294             return (Double) n;
295         }
296         return Double.valueOf(n.doubleValue());
297     }
298 
299     /**
300      * Converts the specified object into a Duration.
301      *
302      * @param value the value to convert
303      * @return the converted value
304      * @throws ConversionException thrown if the value cannot be converted to a Duration
305      * @since 2.8.0
306      */
307     public static Duration toDuration(final Object value) throws ConversionException {
308         if (value instanceof Duration) {
309             return (Duration) value;
310         }
311         if (value instanceof CharSequence) {
312             try {
313                 return Duration.parse((CharSequence) value);
314             } catch (final DateTimeParseException e) {
315                 throw new ConversionException("Could not convert " + value + " to Duration", e);
316             }
317         }
318         throw new ConversionException("The value " + value + " can't be converted to a Duration");
319     }
320 
321     /**
322      * Converts the specified object into a BigInteger.
323      *
324      * @param value the value to convert
325      * @return the converted value
326      * @throws ConversionException thrown if the value cannot be converted to a BigInteger
327      */
328     public static BigInteger toBigInteger(final Object value) throws ConversionException {
329         final Number n = toNumber(value, BigInteger.class);
330         if (n instanceof BigInteger) {
331             return (BigInteger) n;
332         }
333         return BigInteger.valueOf(n.longValue());
334     }
335 
336     /**
337      * Converts the specified object into a BigDecimal.
338      *
339      * @param value the value to convert
340      * @return the converted value
341      * @throws ConversionException thrown if the value cannot be converted to a BigDecimal
342      */
343     public static BigDecimal toBigDecimal(final Object value) throws ConversionException {
344         final Number n = toNumber(value, BigDecimal.class);
345         if (n instanceof BigDecimal) {
346             return (BigDecimal) n;
347         }
348         return BigDecimal.valueOf(n.doubleValue());
349     }
350 
351     /**
352      * Tries to convert the specified object into a number object. This method is used by the conversion methods for number
353      * types. Note that the return value is not in always of the specified target class, but only if a new object has to be
354      * created.
355      *
356      * @param value the value to be converted (must not be <b>null</b>)
357      * @param targetClass the target class of the conversion (must be derived from {@link Number})
358      * @return the converted number
359      * @throws ConversionException if the object cannot be converted
360      */
361     static Number toNumber(final Object value, final Class<?> targetClass) throws ConversionException {
362         if (value instanceof Number) {
363             return (Number) value;
364         }
365         final String str = value.toString();
366         if (str.startsWith(HEX_PREFIX)) {
367             try {
368                 return new BigInteger(str.substring(HEX_PREFIX.length()), HEX_RADIX);
369             } catch (final NumberFormatException nex) {
370                 throw new ConversionException("Could not convert " + str + " to " + targetClass.getName() + "! Invalid hex number.", nex);
371             }
372         }
373 
374         if (str.startsWith(BIN_PREFIX)) {
375             try {
376                 return new BigInteger(str.substring(BIN_PREFIX.length()), BIN_RADIX);
377             } catch (final NumberFormatException nex) {
378                 throw new ConversionException("Could not convert " + str + " to " + targetClass.getName() + "! Invalid binary number.", nex);
379             }
380         }
381 
382         try {
383             final Constructor<?> constr = targetClass.getConstructor(CONSTR_ARGS);
384             return (Number) constr.newInstance(str);
385         } catch (final InvocationTargetException itex) {
386             throw new ConversionException("Could not convert " + str + " to " + targetClass.getName(), itex.getTargetException());
387         } catch (final Exception ex) {
388             // Treat all possible exceptions the same way
389             throw new ConversionException("Conversion error when trying to convert " + str + " to " + targetClass.getName(), ex);
390         }
391     }
392 
393     /**
394      * Converts the specified object into a File.
395      *
396      * @param value the value to convert
397      * @return the converted value
398      * @throws ConversionException thrown if the value cannot be converted to a File
399      * @since 2.3
400      */
401     public static File toFile(final Object value) throws ConversionException {
402         if (value instanceof File) {
403             return (File) value;
404         }
405         if (value instanceof Path) {
406             return ((Path) value).toFile();
407         }
408         if (value instanceof String) {
409             return new File((String) value);
410         }
411         throw new ConversionException("The value " + value + " can't be converted to a File");
412     }
413 
414     /**
415      * Converts the specified object into a Path.
416      *
417      * @param value the value to convert
418      * @return the converted value
419      * @throws ConversionException thrown if the value cannot be converted to a Path
420      * @since 2.3
421      */
422     public static Path toPath(final Object value) throws ConversionException {
423         if (value instanceof File) {
424             return ((File) value).toPath();
425         }
426         if (value instanceof Path) {
427             return (Path) value;
428         }
429         if (value instanceof String) {
430             return Paths.get((String) value);
431         }
432         throw new ConversionException("The value " + value + " can't be converted to a Path");
433     }
434 
435     /**
436      * Converts the specified object into an URI.
437      *
438      * @param value the value to convert
439      * @return the converted value
440      * @throws ConversionException thrown if the value cannot be converted to an URI
441      */
442     public static URI toURI(final Object value) throws ConversionException {
443         if (value instanceof URI) {
444             return (URI) value;
445         }
446         if (!(value instanceof String)) {
447             throw new ConversionException("The value " + value + " can't be converted to an URI");
448         }
449         try {
450             return new URI((String) value);
451         } catch (final URISyntaxException e) {
452             throw new ConversionException("The value " + value + " can't be converted to an URI", e);
453         }
454     }
455 
456     /**
457      * Converts the specified object into an URL.
458      *
459      * @param value the value to convert
460      * @return the converted value
461      * @throws ConversionException thrown if the value cannot be converted to an URL
462      */
463     public static URL toURL(final Object value) throws ConversionException {
464         if (value instanceof URL) {
465             return (URL) value;
466         }
467         if (!(value instanceof String)) {
468             throw new ConversionException("The value " + value + " can't be converted to an URL");
469         }
470         try {
471             return new URL((String) value);
472         } catch (final MalformedURLException e) {
473             throw new ConversionException("The value " + value + " can't be converted to an URL", e);
474         }
475     }
476 
477     /**
478      * Converts the specified object into a Pattern.
479      *
480      * @param value the value to convert
481      * @return the converted value
482      * @throws ConversionException thrown if the value cannot be converted to a Pattern
483      */
484     public static Pattern toPattern(final Object value) throws ConversionException {
485         if (value instanceof Pattern) {
486             return (Pattern) value;
487         }
488         if (!(value instanceof String)) {
489             throw new ConversionException("The value " + value + " can't be converted to a Pattern");
490         }
491         try {
492             return Pattern.compile((String) value);
493         } catch (final PatternSyntaxException e) {
494             throw new ConversionException("The value " + value + " can't be converted to a Pattern", e);
495         }
496     }
497 
498     /**
499      * Converts the specified object into a Locale.
500      *
501      * @param value the value to convert
502      * @return the converted value
503      * @throws ConversionException thrown if the value cannot be converted to a Locale
504      */
505     public static Locale toLocale(final Object value) throws ConversionException {
506         if (value instanceof Locale) {
507             return (Locale) value;
508         }
509         if (!(value instanceof String)) {
510             throw new ConversionException("The value " + value + " can't be converted to a Locale");
511         }
512         final String[] elements = ((String) value).split("_");
513         final int size = elements.length;
514 
515         if (size >= 1 && (elements[0].length() == 2 || elements[0].isEmpty())) {
516             final String language = elements[0];
517             final String country = size >= 2 ? elements[1] : "";
518             final String variant = size >= 3 ? elements[2] : "";
519 
520             return new Locale(language, country, variant);
521         }
522         throw new ConversionException("The value " + value + " can't be converted to a Locale");
523     }
524 
525     /**
526      * Converts the specified object into a Color. If the value is a String, the format allowed is
527      * (#)?[0-9A-F]{6}([0-9A-F]{2})?. Examples:
528      * <ul>
529      * <li>FF0000 (red)</li>
530      * <li>0000FFA0 (semi transparent blue)</li>
531      * <li>#CCCCCC (gray)</li>
532      * <li>#00FF00A0 (semi transparent green)</li>
533      * </ul>
534      *
535      * @param value the value to convert
536      * @return the converted value
537      * @throws ConversionException thrown if the value cannot be converted to a Color
538      */
539     public static Color toColor(final Object value) throws ConversionException {
540         if (value instanceof Color) {
541             return (Color) value;
542         }
543         if (!(value instanceof String) || StringUtils.isBlank((String) value)) {
544             throw new ConversionException("The value " + value + " can't be converted to a Color");
545         }
546         String color = ((String) value).trim();
547 
548         final int[] components = new int[3];
549 
550         // check the size of the string
551         final int minlength = components.length * 2;
552         if (color.length() < minlength) {
553             throw new ConversionException("The value " + value + " can't be converted to a Color");
554         }
555 
556         // remove the leading #
557         if (color.startsWith("#")) {
558             color = color.substring(1);
559         }
560 
561         try {
562             // parse the components
563             for (int i = 0; i < components.length; i++) {
564                 components[i] = Integer.parseInt(color.substring(2 * i, 2 * i + 2), HEX_RADIX);
565             }
566 
567             // parse the transparency
568             final int alpha;
569             if (color.length() >= minlength + 2) {
570                 alpha = Integer.parseInt(color.substring(minlength, minlength + 2), HEX_RADIX);
571             } else {
572                 alpha = Color.black.getAlpha();
573             }
574 
575             return new Color(components[0], components[1], components[2], alpha);
576         } catch (final Exception e) {
577             throw new ConversionException("The value " + value + " can't be converted to a Color", e);
578         }
579     }
580 
581     /**
582      * Converts the specified value into an internet address.
583      *
584      * @param value the value to convert
585      * @return the converted value
586      * @throws ConversionException thrown if the value cannot be converted to a InetAddress
587      *
588      * @since 1.5
589      */
590     static InetAddress toInetAddress(final Object value) throws ConversionException {
591         if (value instanceof InetAddress) {
592             return (InetAddress) value;
593         }
594         if (!(value instanceof String)) {
595             throw new ConversionException("The value " + value + " can't be converted to a InetAddress");
596         }
597         try {
598             return InetAddress.getByName((String) value);
599         } catch (final UnknownHostException e) {
600             throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e);
601         }
602     }
603 
604     /**
605      * Converts the specified value into an email address with the given class name.
606      *
607      * @param value the value to convert
608      * @param targetClassName the fully qualified name of the {@code InternetAddress} class to convert to, e.g.,
609      *      {@value #INTERNET_ADDRESS_CLASSNAME_JAVAX} or {@value #INTERNET_ADDRESS_CLASSNAME_JAKARTA}
610      * @return the converted value
611      * @throws ConversionException thrown if the value cannot be converted to an email address
612      *
613      * @since 1.5
614      */
615     static Object toInternetAddress(final Object value, final String targetClassName) throws ConversionException {
616         if (value.getClass().getName().equals(targetClassName)) {
617             return value;
618         }
619         if (!(value instanceof String)) {
620             throw new ConversionException("The value " + value + " can't be converted to an InternetAddress");
621         }
622         try {
623             final Constructor<?> ctor = Class.forName(targetClassName).getConstructor(String.class);
624             return ctor.newInstance(value);
625         } catch (final Exception e) {
626             throw new ConversionException("The value " + value + " can't be converted to an InternetAddress", e);
627         }
628     }
629 
630     /**
631      * Converts the specified value into an {@link Enum}.
632      *
633      * @param value the value to convert
634      * @param cls the type of the enumeration
635      * @return the converted value
636      * @throws ConversionException thrown if the value cannot be converted to an enumeration
637      *
638      * @since 1.5
639      */
640     static <E extends Enum<E>> E toEnum(final Object value, final Class<E> cls) throws ConversionException {
641         if (value.getClass().equals(cls)) {
642             return cls.cast(value);
643         }
644         if (value instanceof String) {
645             try {
646                 return Enum.valueOf(cls, (String) value);
647             } catch (final Exception e) {
648                 throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
649             }
650         }
651         if (!(value instanceof Number)) {
652             throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
653         }
654         try {
655             final E[] enumConstants = cls.getEnumConstants();
656             return enumConstants[((Number) value).intValue()];
657         } catch (final Exception e) {
658             throw new ConversionException("The value " + value + " can't be converted to a " + cls.getName());
659         }
660     }
661 
662     /**
663      * Converts the specified object into a Date.
664      *
665      * @param value the value to convert
666      * @param format the DateFormat pattern to parse String values
667      * @return the converted value
668      * @throws ConversionException thrown if the value cannot be converted to a Calendar
669      */
670     public static Date toDate(final Object value, final String format) throws ConversionException {
671         if (value instanceof Date) {
672             return (Date) value;
673         }
674         if (value instanceof Calendar) {
675             return ((Calendar) value).getTime();
676         }
677         if (!(value instanceof String)) {
678             throw new ConversionException("The value " + value + " can't be converted to a Date");
679         }
680         try {
681             return new SimpleDateFormat(format).parse((String) value);
682         } catch (final ParseException e) {
683             throw new ConversionException("The value " + value + " can't be converted to a Date", e);
684         }
685     }
686 
687     /**
688      * Converts the specified object into a Calendar.
689      *
690      * @param value the value to convert
691      * @param format the DateFormat pattern to parse String values
692      * @return the converted value
693      * @throws ConversionException thrown if the value cannot be converted to a Calendar
694      */
695     public static Calendar toCalendar(final Object value, final String format) throws ConversionException {
696         if (value instanceof Calendar) {
697             return (Calendar) value;
698         }
699         if (value instanceof Date) {
700             final Calendar calendar = Calendar.getInstance();
701             calendar.setTime((Date) value);
702             return calendar;
703         }
704         if (!(value instanceof String)) {
705             throw new ConversionException("The value " + value + " can't be converted to a Calendar");
706         }
707         try {
708             final Calendar calendar = Calendar.getInstance();
709             calendar.setTime(new SimpleDateFormat(format).parse((String) value));
710             return calendar;
711         } catch (final ParseException e) {
712             throw new ConversionException("The value " + value + " can't be converted to a Calendar", e);
713         }
714     }
715 
716     /**
717      * Converts a value to a constant of an enumeration class.
718      *
719      * @param enumClass the enumeration class
720      * @param value the value to be converted
721      * @return the converted value
722      */
723     @SuppressWarnings("unchecked")
724     // conversion is safe because we know that the class is an Enum class
725     private static Object convertToEnum(final Class<?> enumClass, final Object value) {
726         return toEnum(value, enumClass.asSubclass(Enum.class));
727     }
728 }