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