    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.beanutils2.locale;
    019
    020import java.text.ParseException;
    021import java.util.Locale;
    022
    023import org.apache.commons.beanutils2.ConversionException;
    024import org.apache.commons.beanutils2.ConvertUtils;
    025import org.apache.commons.logging.Log;
    026import org.apache.commons.logging.LogFactory;
    027
    028/**
    029 * The base class for all standard type locale-sensitive converters. It has {@link LocaleConverter} and {@link org.apache.commons.beanutils2.Converter}
    030 * implementations, that convert an incoming locale-sensitive Object into an object of correspond type, optionally using a default value or throwing a
    031 * {@link ConversionException} if a conversion error occurs.
    032 *
    033 * @param <T> The converter type.
    034 */
    035public abstract class BaseLocaleConverter<T> implements LocaleConverter<T> {
    036
    037    /**
    038     * Builds instances of {@link BaseLocaleConverter} subclasses.
    039     *
    040     * @param <B> The builder type.
    041     * @param <T> The converter type.
    042     */
    043    public abstract static class Builder<B extends Builder<B, T>, T> {
    044
    045        /** The default value specified to our Constructor, if any. */
    046        protected T defaultValue;
    047
    048        /** The locale specified to our Constructor, by default - system locale. */
    049        protected Locale locale = Locale.getDefault();
    050
    051        /** The flag indicating whether the given pattern string is localized or not. */
    052        protected boolean localizedPattern;
    053
    054        /** The default pattern specified to our Constructor, if any. */
    055        protected String pattern;
    056
    057        /** Should we return the default value on conversion errors? */
    058        protected boolean useDefault;
    059
    060        /**
    061         * Returns this instance cast as the exact subclass type.
    062         *
    063         * @return this instance cast as the exact subclass type.
    064         */
    065        @SuppressWarnings("unchecked")
    066        protected B asThis() {
    067            return (B) this;
    068        }
    069
    070        /**
    071         * Gets a newly built instance.
    072         *
    073         * @return a newly built instance.
    074         */
    075        public abstract BaseLocaleConverter<?> get();
    076
    077        /**
    078         * Sets the default value.
    079         *
    080         * @param defaultValue the default value.
    081         * @return {@code this} instance.
    082         */
    083        public B setDefault(final T defaultValue) {
    084            this.defaultValue = defaultValue;
    085            return asThis();
    086        }
    087
    088        /**
    089         * Sets the locale.
    090         *
    091         * @param locale the locale.
    092         * @return {@code this} instance.
    093         */
    094        public B setLocale(final Locale locale) {
    095            this.locale = locale;
    096            return asThis();
    097        }
    098
    099        /**
    100         * Sets the localized pattern.
    101         *
    102         * @param localizedPattern the localized pattern.
    103         * @return {@code this} instance.
    104         */
    105        public B setLocalizedPattern(final boolean localizedPattern) {
    106            this.localizedPattern = localizedPattern;
    107            return asThis();
    108        }
    109
    110        /**
    111         * Sets the pattern.
    112         *
    113         * @param pattern the pattern.
    114         * @return {@code this} instance.
    115         */
    116        public B setPattern(final String pattern) {
    117            this.pattern = pattern;
    118            return asThis();
    119        }
    120
    121        /**
    122         * Sets the use of default.
    123         *
    124         * @param useDefault the use of default.
    125         * @return {@code this} instance.
    126         */
    127        public B setUseDefault(final boolean useDefault) {
    128            this.useDefault = useDefault;
    129            return asThis();
    130        }
    131
    132    }
    133
    134    /** All logging goes through this logger */
    135    private static final Log LOG = LogFactory.getLog(BaseLocaleConverter.class);
    136
    137    /**
    138     * Checks whether the result of a conversion is conform to the specified target type. If this is the case, the passed in result object is cast to the
    139     * correct target type. Otherwise, an exception is thrown.
    140     *
    141     * @param <T>    the desired result type
    142     * @param type   the target class of the conversion
    143     * @param result the conversion result object
    144     * @return the result cast to the target class
    145     * @throws ConversionException if the result object is not compatible with the target type
    146     */
    147    private static <R> R checkConversionResult(final Class<R> type, final Object result) {
    148        if (type == null) {
    149            // In this case we cannot do much: The result object is returned.
    150            return (R) result;
    151        }
    152
    153        if (result == null) {
    154            return null;
    155        }
    156        if (type.isInstance(result)) {
    157            return type.cast(result);
    158        }
    159        throw new ConversionException("Unsupported target type: " + type);
    160    }
    161
    162    /** The default value specified to our Constructor, if any. */
    163    protected final T defaultValue;
    164
    165    /** The locale specified to our Constructor, by default - system locale. */
    166    protected final Locale locale;
    167
    168    /** The flag indicating whether the given pattern string is localized or not. */
    169    protected final boolean localizedPattern;
    170
    171    /** The default pattern specified to our Constructor, if any. */
    172    protected final String pattern;
    173
    174    /** Should we return the default value on conversion errors? */
    175    protected final boolean useDefault;
    176
    177    /**
    178     * Constructs a {@link LocaleConverter} that will return the specified default value or throw a {@link ConversionException} if a conversion error occurs.
    179     *
    180     * @param defaultValue The default value to be returned
    181     * @param locale       The locale
    182     * @param pattern      The conversion pattern
    183     * @param useDefault   Indicate whether the default value is used or not
    184     * @param locPattern   Indicate whether the pattern is localized or not
    185     */
    186    protected BaseLocaleConverter(final T defaultValue, final Locale locale, final String pattern, final boolean useDefault, final boolean locPattern) {
    187        this.defaultValue = useDefault ? defaultValue : null;
    188        this.useDefault = useDefault;
    189        this.locale = locale != null ? locale : Locale.getDefault();
    190        this.pattern = pattern;
    191        this.localizedPattern = locPattern;
    192    }
    193
    194    /**
    195     * Converts the specified locale-sensitive input object into an output object of the specified type. The default pattern is used for the conversion.
    196     *
    197     * @param type  Data type to which this value should be converted
    198     * @param value The input object to be converted
    199     * @return The converted value
    200     * @throws ConversionException if conversion cannot be performed successfully
    201     */
    202    @Override
    203    public <R> R convert(final Class<R> type, final Object value) {
    204        return convert(type, value, null);
    205    }
    206
    207    /**
    208     * Converts the specified locale-sensitive input object into an output object of the specified type.
    209     *
    210     * @param type    Data is type to which this value should be converted
    211     * @param value   is the input object to be converted
    212     * @param pattern is the pattern is used for the conversion; if null is passed then the default pattern associated with the converter object will be used.
    213     * @return The converted value
    214     * @throws ConversionException if conversion cannot be performed successfully
    215     */
    216    @Override
    217    public <R> R convert(final Class<R> type, final Object value, final String pattern) {
    218        final Class<R> targetType = ConvertUtils.primitiveToWrapper(type);
    219        if (value == null) {
    220            if (useDefault) {
    221                return getDefaultAs(targetType);
    222            }
    223            // symmetric BeanUtils function allows null
    224            // so do not: throw new ConversionException("No value specified");
    225            LOG.debug("Null value specified for conversion, returning null");
    226            return null;
    227        }
    228
    229        try {
    230            if (pattern != null) {
    231                return checkConversionResult(targetType, parse(value, pattern));
    232            }
    233            return checkConversionResult(targetType, parse(value, this.pattern));
    234        } catch (final Exception e) {
    235            if (useDefault) {
    236                return getDefaultAs(targetType);
    237            }
    238            if (e instanceof ConversionException) {
    239                throw (ConversionException) e;
    240            }
    241            throw new ConversionException(e);
    242        }
    243    }
    244
    245    /**
    246     * Converts the specified locale-sensitive input object into an output object. The default pattern is used for the conversion.
    247     *
    248     * @param value The input object to be converted
    249     * @return The converted value
    250     * @throws ConversionException if conversion cannot be performed successfully
    251     */
    252    public Object convert(final Object value) {
    253        return convert(value, null);
    254    }
    255
    256    /**
    257     * Converts the specified locale-sensitive input object into an output object.
    258     *
    259     * @param value   The input object to be converted
    260     * @param pattern The pattern is used for the conversion
    261     * @return The converted value
    262     * @throws ConversionException if conversion cannot be performed successfully
    263     */
    264    public T convert(final Object value, final String pattern) {
    265        return convert(null, value, pattern);
    266    }
    267
    268    /**
    269     * Gets the default object specified for this converter cast for the given target type. If the default value is not conform to the given type, an exception
    270     * is thrown.
    271     *
    272     * @param type the target class of the conversion
    273     * @return the default value in the given target type
    274     * @throws ConversionException if the default object is not compatible with the target type
    275     */
    276    private <R> R getDefaultAs(final Class<R> type) {
    277        return checkConversionResult(type, defaultValue);
    278    }
    279
    280    /**
    281     * Converts the specified locale-sensitive input object into an output object of the specified type.
    282     *
    283     * @param value   The input object to be converted
    284     * @param pattern The pattern is used for the conversion
    285     * @return The converted value
    286     * @throws ParseException if conversion cannot be performed successfully
    287     */
    288    abstract protected T parse(Object value, String pattern) throws ParseException;
    289}