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}