    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
    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;
    048
    049/**
    050 * A utility class to convert the configuration properties into any type.
    051 *
    052 * @since 2.8.0
    053 */
    054public final class PropertyConverter {
    055
    056    /** Constant for the prefix of hex numbers. */
    057    private static final String HEX_PREFIX = "0x";
    058
    059    /** Constant for the radix of hex numbers. */
    060    private static final int HEX_RADIX = 16;
    061
    062    /** Constant for the prefix of binary numbers. */
    063    private static final String BIN_PREFIX = "0b";
    064
    065    /** Constant for the radix of binary numbers. */
    066    private static final int BIN_RADIX = 2;
    067
    068    /** Constant for the argument classes of the Number constructor that takes a String. */
    069    private static final Class<?>[] CONSTR_ARGS = {String.class};
    070
    071    /** The fully qualified name of {@code javax.mail.internet.InternetAddress}, as used in the javamail-1.* API.  */
    072    private static final String INTERNET_ADDRESS_CLASSNAME_JAVAX = "javax.mail.internet.InternetAddress";
    073
    074    /** The fully qualified name of {@code jakarta.mail.internet.InternetAddress}, as used in the javamail-2.0+ API. */
    075    private static final String INTERNET_ADDRESS_CLASSNAME_JAKARTA = "jakarta.mail.internet.InternetAddress";
    076
    077    /**
    078     * Converts a value to a constant of an enumeration class.
    079     *
    080     * @param enumClass the enumeration class
    081     * @param value the value to be converted
    082     * @return the converted value
    083     */
    084    @SuppressWarnings("unchecked")
    085    // conversion is safe because we know that the class is an Enum class
    086    private static Object convertToEnum(final Class<?> enumClass, final Object value) {
    087        return toEnum(value, enumClass.asSubclass(Enum.class));
    088    }
    089
    090    /**
    091     * Converts the specified value object to the given target data class. If additional
    092     * information is required for this conversion, it is obtained from the passed in {@code DefaultConversionHandler}
    093     * object. If the class is a primitive type (Integer.TYPE, Boolean.TYPE, etc), the value returned will use the wrapper
    094     * type (Integer.class, Boolean.class, etc).
    095     *
    096     * @param cls the target class of the converted value
    097     * @param value the value to convert
    098     * @param convHandler the conversion handler object
    099     * @return the converted value
    100     * @throws ConversionException if the value is not compatible with the requested type
    101     */
    102    public static Object to(final Class<?> cls, final Object value, final DefaultConversionHandler convHandler) throws ConversionException {
    103        if (cls.isInstance(value)) {
    104            return value; // no conversion needed
    105        }
    106
    107        if (String.class.equals(cls)) {
    108            return String.valueOf(value);
    109        }
    110        if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) {
    111            return toBoolean(value);
    112        }
    113        if (Character.class.equals(cls) || Character.TYPE.equals(cls)) {
    114            return toCharacter(value);
    115        }
    116        if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) {
    117            if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) {
    118                return toInteger(value);
    119            }
    120            if (Long.class.equals(cls) || Long.TYPE.equals(cls)) {
    121                return toLong(value);
    122            }
    123            if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) {
    124                return toByte(value);
    125            }
    126            if (Short.class.equals(cls) || Short.TYPE.equals(cls)) {
    127                return toShort(value);
    128            }
    129            if (Float.class.equals(cls) || Float.TYPE.equals(cls)) {
    130                return toFloat(value);
    131            }
    132            if (Double.class.equals(cls) || Double.TYPE.equals(cls)) {
    133                return toDouble(value);
    134            }
    135            if (BigInteger.class.equals(cls)) {
    136                return toBigInteger(value);
    137            }
    138            if (BigDecimal.class.equals(cls)) {
    139                return toBigDecimal(value);
    140            }
    141            return toNumber(value, cls);
    142        }
    143        if (Date.class.equals(cls)) {
    144            return toDate(value, convHandler.getDateFormat());
    145        }
    146        if (Calendar.class.equals(cls)) {
    147            return toCalendar(value, convHandler.getDateFormat());
    148        }
    149        if (File.class.equals(cls)) {
    150            return toFile(value);
    151        }
    152        if (Path.class.equals(cls)) {
    153            return toPath(value);
    154        }
    155        if (URI.class.equals(cls)) {
    156            return toURI(value);
    157        }
    158        if (URL.class.equals(cls)) {
    159            return toURL(value);
    160        }
    161        if (Pattern.class.equals(cls)) {
    162            return toPattern(value);
    163        }
    164        if (Locale.class.equals(cls)) {
    165            return toLocale(value);
    166        }
    167        if (cls.isEnum()) {
    168            return convertToEnum(cls, value);
    169        }
    170        if (Color.class.equals(cls)) {
    171            return toColor(value);
    172        }
    173        if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAVAX)) {
    174            // javamail-1.* With javax.mail.* namespace.
    175            return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAVAX);
    176        }
    177        if (cls.getName().equals(INTERNET_ADDRESS_CLASSNAME_JAKARTA)) {
    178            // javamail-2.0+, with jakarta.mail.* namespace.
    179            return toInternetAddress(value, INTERNET_ADDRESS_CLASSNAME_JAKARTA);
    180        }
    181        if (InetAddress.class.isAssignableFrom(cls)) {
    182            return toInetAddress(value);
    183        }
    184        if (Duration.class.equals(cls)) {
    185            return toDuration(value);
    186        }
    187
    188        throw new ConversionException("The value '" + value + "' (" + value.getClass() + ")" + " can't be converted to a " + cls.getName() + " object");
    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 " + value + " can't be converted to a Boolean object");
    237        }
    238        final Boolean b = BooleanUtils.toBooleanObject((String) value);
    239        if (b == null) {
    240            throw new ConversionException("The value " + value + " can't be converted to a Boolean object");
    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 " + value + " can't be converted to a Calendar");
    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("The value " + value + " can't be converted to a Calendar", e);
    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(String.format("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 " + value + " can't be converted to a Color");
    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 " + value + " can't be converted to a Color");
    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("The value " + value + " can't be converted to a Color", e);
    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 " + value + " can't be converted to a Date");
    378        }
    379        try {
    380            return new SimpleDateFormat(format).parse((String) value);
    381        } catch (final ParseException e) {
    382            throw new ConversionException("The value " + value + " can't be converted to a Date", e);
    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("Could not convert " + value + " to Duration", e);
    418            }
    419        }
    420        throw new ConversionException("The value " + value + " can't be converted to a Duration");
    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("The value " + value + " can't be converted to a " + cls.getName());
    441            }
    442        }
    443        if (!(value instanceof Number)) {
    444            throw new ConversionException("The value " + value + " can't be converted to a " + 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("The value " + value + " can't be converted to a " + 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 " + value + " can't be converted to a File");
    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 " + value + " can't be converted to a InetAddress");
    504        }
    505        try {
    506            return InetAddress.getByName((String) value);
    507        } catch (final UnknownHostException e) {
    508            throw new ConversionException("The value " + value + " can't be converted to a InetAddress", e);
    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 " + value + " can't be converted to an InternetAddress");
    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("The value " + value + " can't be converted to an InternetAddress", e);
    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 " + value + " can't be converted to a Locale");
    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 " + value + " can't be converted to a Locale");
    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 (StringUtils.startsWithAny(str, HEX_PREFIX)) {
    610            try {
    611                return new BigInteger(str.substring(HEX_PREFIX.length()), HEX_RADIX);
    612            } catch (final NumberFormatException nex) {
    613                throw new ConversionException("Could not convert " + str + " to " + targetClass.getName() + "! Invalid hex number.", nex);
    614            }
    615        }
    616
    617        if (StringUtils.startsWithAny(str, BIN_PREFIX)) {
    618            try {
    619                return new BigInteger(str.substring(BIN_PREFIX.length()), BIN_RADIX);
    620            } catch (final NumberFormatException nex) {
    621                throw new ConversionException("Could not convert " + str + " to " + targetClass.getName() + "! Invalid binary number.", nex);
    622            }
    623        }
    624
    625        try {
    626            final Constructor<?> constr = targetClass.getConstructor(CONSTR_ARGS);
    627            return (Number) constr.newInstance(str);
    628        } catch (final InvocationTargetException itex) {
    629            throw new ConversionException("Could not convert " + str + " to " + targetClass.getName(), itex.getTargetException());
    630        } catch (final Exception ex) {
    631            // Treat all possible exceptions the same way
    632            throw new ConversionException("Conversion error when trying to convert " + str + " to " + targetClass.getName(), ex);
    633        }
    634    }
    635
    636    /**
    637     * Converts the specified object into a Path.
    638     *
    639     * @param value the value to convert
    640     * @return the converted value
    641     * @throws ConversionException thrown if the value cannot be converted to a Path
    642     * @since 2.3
    643     */
    644    public static Path toPath(final Object value) throws ConversionException {
    645        if (value instanceof File) {
    646            return ((File) value).toPath();
    647        }
    648        if (value instanceof Path) {
    649            return (Path) value;
    650        }
    651        if (value instanceof String) {
    652            return Paths.get((String) value);
    653        }
    654        throw new ConversionException("The value " + value + " can't be converted to a Path");
    655    }
    656
    657    /**
    658     * Converts the specified object into a Pattern.
    659     *
    660     * @param value the value to convert
    661     * @return the converted value
    662     * @throws ConversionException thrown if the value cannot be converted to a Pattern
    663     */
    664    public static Pattern toPattern(final Object value) throws ConversionException {
    665        if (value instanceof Pattern) {
    666            return (Pattern) value;
    667        }
    668        if (!(value instanceof String)) {
    669            throw new ConversionException("The value " + value + " can't be converted to a Pattern");
    670        }
    671        try {
    672            return Pattern.compile((String) value);
    673        } catch (final PatternSyntaxException e) {
    674            throw new ConversionException("The value " + value + " can't be converted to a Pattern", e);
    675        }
    676    }
    677
    678    /**
    679     * Converts the specified object into a Short.
    680     *
    681     * @param value the value to convert
    682     * @return the converted value
    683     * @throws ConversionException thrown if the value cannot be converted to a short
    684     */
    685    public static Short toShort(final Object value) throws ConversionException {
    686        final Number n = toNumber(value, Short.class);
    687        if (n instanceof Short) {
    688            return (Short) n;
    689        }
    690        return n.shortValue();
    691    }
    692
    693    /**
    694     * Converts the specified object into an URI.
    695     *
    696     * @param value the value to convert
    697     * @return the converted value
    698     * @throws ConversionException thrown if the value cannot be converted to an URI
    699     */
    700    public static URI toURI(final Object value) throws ConversionException {
    701        if (value instanceof URI) {
    702            return (URI) value;
    703        }
    704        if (!(value instanceof String)) {
    705            throw new ConversionException("The value " + value + " can't be converted to an URI");
    706        }
    707        try {
    708            return new URI((String) value);
    709        } catch (final URISyntaxException e) {
    710            throw new ConversionException("The value " + value + " can't be converted to an URI", e);
    711        }
    712    }
    713
    714    /**
    715     * Converts the specified object into an URL.
    716     *
    717     * @param value the value to convert
    718     * @return the converted value
    719     * @throws ConversionException thrown if the value cannot be converted to an URL
    720     */
    721    public static URL toURL(final Object value) throws ConversionException {
    722        if (value instanceof URL) {
    723            return (URL) value;
    724        }
    725        if (!(value instanceof String)) {
    726            throw new ConversionException("The value " + value + " can't be converted to an URL");
    727        }
    728        try {
    729            return new URL((String) value);
    730        } catch (final MalformedURLException e) {
    731            throw new ConversionException("The value " + value + " can't be converted to an URL", e);
    732        }
    733    }
    734
    735    /**
    736     * Private constructor prevents instances from being created.
    737     */
    738    private PropertyConverter() {
    739        // presvents instantiation.
    740    }
    741}