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