View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */ 
16   
17  package org.apache.commons.beanutils.locale;
18  
19  import org.apache.commons.beanutils.locale.converters.*;
20  import org.apache.commons.beanutils.Converter;
21  import org.apache.commons.collections.FastHashMap;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  import java.lang.reflect.Array;
26  import java.math.BigDecimal;
27  import java.math.BigInteger;
28  import java.sql.Date;
29  import java.sql.Time;
30  import java.sql.Timestamp;
31  import java.util.Locale;
32  
33  /**
34   * <p>Utility methods for converting locale-sensitive String scalar values to objects of the
35   * specified Class, String arrays to arrays of the specified Class and
36   * object to locale-sensitive String scalar value.</p>
37   *
38   * <p>This class provides the implementations used by the static utility methods in 
39   * {@link LocaleConvertUtils}.</p>
40   * 
41   * <p>The actual {@link LocaleConverter} instance to be used
42   * can be registered for each possible destination Class. Unless you override them, standard
43   * {@link LocaleConverter} instances are provided for all of the following
44   * destination Classes:</p>
45   * <ul>
46   * <li>java.lang.BigDecimal</li>
47   * <li>java.lang.BigInteger</li>
48   * <li>byte and java.lang.Byte</li>
49   * <li>double and java.lang.Double</li>
50   * <li>float and java.lang.Float</li>
51   * <li>int and java.lang.Integer</li>
52   * <li>long and java.lang.Long</li>
53   * <li>short and java.lang.Short</li>
54   * <li>java.lang.String</li>
55   * <li>java.sql.Date</li>
56   * <li>java.sql.Time</li>
57   * <li>java.sql.Timestamp</li>
58   * </ul>
59   *
60   * <p>For backwards compatibility, the standard locale converters
61   * for primitive types (and the corresponding wrapper classes).
62   *
63   * If you prefer to have another {@link LocaleConverter}
64   * thrown instead, replace the standard {@link LocaleConverter} instances
65   * with ones created with the one of the appropriate constructors.
66   *
67   * It's important that {@link LocaleConverter} should be registered for
68   * the specified locale and Class (or primitive type).
69   *
70   * @author Yauheny Mikulski
71   * @since 1.7
72   */
73  public class LocaleConvertUtilsBean {
74      
75      /** 
76       * Gets singleton instance.
77       * This is the same as the instance used by the default {@link LocaleBeanUtilsBean} singleton.
78       */
79      public static LocaleConvertUtilsBean getInstance() {
80          return LocaleBeanUtilsBean.getLocaleBeanUtilsInstance().getLocaleConvertUtils();
81      }
82  
83      // ----------------------------------------------------- Instance Variables
84  
85      /** The locale - default for convertion. */
86      private Locale defaultLocale = Locale.getDefault();
87  
88      /** Indicate whether the pattern is localized or not */
89      private boolean applyLocalized = false;
90  
91      /** The <code>Log</code> instance for this class. */
92      private Log log = LogFactory.getLog(LocaleConvertUtils.class);
93  
94      /** Every entry of the mapConverters is:
95       *  key = locale
96       *  value = FastHashMap of converters for the certain locale.
97       */
98      private FastHashMap mapConverters = new FastHashMap();
99  
100     // --------------------------------------------------------- Constructors
101 
102     /**
103      *  Makes the state by default (deregisters all converters for all locales)
104      *  and then registers default locale converters.
105      */
106     public LocaleConvertUtilsBean() {
107         deregister();
108     }
109     
110     // --------------------------------------------------------- Properties
111      
112     /**
113      * getter for defaultLocale
114      */
115     public Locale getDefaultLocale() {
116 
117         return defaultLocale;
118     }
119 
120     /**
121      * setter for defaultLocale
122      */
123     public void setDefaultLocale(Locale locale) {
124 
125         if (locale == null) {
126             defaultLocale = Locale.getDefault();
127         }
128         else {
129             defaultLocale = locale;
130         }
131     }
132     
133     /**
134      * getter for applyLocalized
135      */
136     public boolean getApplyLocalized() {
137         return applyLocalized;
138     }
139 
140     /**
141      * setter for applyLocalized
142      */
143     public void setApplyLocalized(boolean newApplyLocalized) {
144         applyLocalized = newApplyLocalized;
145     }
146 
147     // --------------------------------------------------------- Methods
148 
149     /**
150      * Convert the specified locale-sensitive value into a String.
151      *
152      * @param value The Value to be converted
153      *
154      * @exception org.apache.commons.beanutils.ConversionException if thrown by an underlying Converter
155      */
156     public String convert(Object value) {
157         return convert(value, defaultLocale, null);
158     }
159 
160     /**
161      * Convert the specified locale-sensitive value into a String
162      * using the convertion pattern.
163      *
164      * @param value The Value to be converted
165      * @param pattern       The convertion pattern
166      *
167      * @exception ConversionException if thrown by an underlying Converter
168      */
169     public String convert(Object value, String pattern) {
170         return convert(value, defaultLocale, pattern);
171     }
172 
173     /**
174      * Convert the specified locale-sensitive value into a String
175      * using the paticular convertion pattern.
176      *
177      * @param value The Value to be converted
178      * @param locale The locale
179      * @param pattern The convertion pattern
180      *
181      * @exception ConversionException if thrown by an underlying Converter
182      */
183     public String convert(Object value, Locale locale, String pattern) {
184 
185         LocaleConverter converter = lookup(String.class, locale);
186 		
187         return (String) converter.convert(String.class, value, pattern);
188     }
189 
190     /**
191      * Convert the specified value to an object of the specified class (if
192      * possible).  Otherwise, return a String representation of the value.
193      *
194      * @param value The String scalar value to be converted
195      * @param clazz The Data type to which this value should be converted.
196      *
197      * @exception ConversionException if thrown by an underlying Converter
198      */
199     public Object convert(String value, Class clazz) {
200 
201         return convert(value, clazz, defaultLocale, null);
202     }
203 
204     /**
205      * Convert the specified value to an object of the specified class (if
206      * possible) using the convertion pattern. Otherwise, return a String
207      * representation of the value.
208      *
209      * @param value The String scalar value to be converted
210      * @param clazz The Data type to which this value should be converted.
211      * @param pattern The convertion pattern
212      *
213      * @exception ConversionException if thrown by an underlying Converter
214      */
215     public Object convert(String value, Class clazz, String pattern) {
216 
217         return convert(value, clazz, defaultLocale, pattern);
218     }
219 
220     /**
221      * Convert the specified value to an object of the specified class (if
222      * possible) using the convertion pattern. Otherwise, return a String
223      * representation of the value.
224      *
225      * @param value The String scalar value to be converted
226      * @param clazz The Data type to which this value should be converted.
227      * @param locale The locale
228      * @param pattern The convertion pattern
229      *
230      * @exception ConversionException if thrown by an underlying Converter
231      */
232     public Object convert(String value, Class clazz, Locale locale, String pattern) {
233 
234         if (log.isDebugEnabled()) {
235             log.debug("Convert string " + value + " to class " +
236                     clazz.getName() + " using " + locale.toString() +
237                     " locale and " + pattern + " pattern");
238         }
239 
240         LocaleConverter converter = lookup(clazz, locale);
241 
242         if (converter == null) {
243             converter = (LocaleConverter) lookup(String.class, locale);
244         }
245         if (log.isTraceEnabled()) {
246             log.trace("  Using converter " + converter);
247         }
248 
249         return (converter.convert(clazz, value, pattern));
250     }
251 
252     /**
253      * Convert an array of specified values to an array of objects of the
254      * specified class (if possible) using the convertion pattern.
255      *
256      * @param values Value to be converted (may be null)
257      * @param clazz Java array or element class to be converted to
258      * @param pattern The convertion pattern
259      *
260      * @exception ConversionException if thrown by an underlying Converter
261      */
262     public Object convert(String values[], Class clazz, String pattern) {
263 
264         return convert(values, clazz, getDefaultLocale(), pattern);
265     }
266 
267    /**
268     * Convert an array of specified values to an array of objects of the
269     * specified class (if possible) .
270     *
271     * @param values Value to be converted (may be null)
272     * @param clazz Java array or element class to be converted to
273     *
274     * @exception ConversionException if thrown by an underlying Converter
275     */
276    public Object convert(String values[], Class clazz) {
277 
278        return convert(values, clazz, getDefaultLocale(), null);
279    }
280 
281     /**
282      * Convert an array of specified values to an array of objects of the
283      * specified class (if possible) using the convertion pattern.
284      *
285      * @param values Value to be converted (may be null)
286      * @param clazz Java array or element class to be converted to
287      * @param locale The locale
288      * @param pattern The convertion pattern
289      *
290      * @exception ConversionException if thrown by an underlying Converter
291      */
292     public Object convert(String values[], Class clazz, Locale locale, String pattern) {
293 
294         Class type = clazz;
295         if (clazz.isArray()) {
296             type = clazz.getComponentType();
297         }
298         if (log.isDebugEnabled()) {
299             log.debug("Convert String[" + values.length + "] to class " +
300                     type.getName() + "[] using " + locale.toString() +
301                     " locale and " + pattern + " pattern");
302         }
303 
304         Object array = Array.newInstance(type, values.length);
305         for (int i = 0; i < values.length; i++) {
306             Array.set(array, i, convert(values[i], type, locale, pattern));
307         }
308 
309         return (array);
310     }
311 
312     /**
313      * Register a custom {@link LocaleConverter} for the specified destination
314      * <code>Class</code>, replacing any previously registered converter.
315      *
316      * @param converter The LocaleConverter to be registered
317      * @param clazz The Destination class for conversions performed by this
318      *  Converter
319      * @param locale The locale
320      */
321     public void register(LocaleConverter converter, Class clazz, Locale locale) {
322 
323         lookup(locale).put(clazz, converter);
324     }
325 
326     /**
327      * Remove any registered {@link LocaleConverter}.
328      */
329     public void deregister() {
330 
331         FastHashMap defaultConverter = lookup(defaultLocale);
332 
333         mapConverters.setFast(false);
334 
335         mapConverters.clear();
336         mapConverters.put(defaultLocale, defaultConverter);
337 
338         mapConverters.setFast(true);
339     }
340 
341 
342     /**
343      * Remove any registered {@link LocaleConverter} for the specified locale
344      *
345      * @param locale The locale
346      */
347     public void deregister(Locale locale) {
348 
349         mapConverters.remove(locale);
350     }
351 
352 
353     /**
354      * Remove any registered {@link LocaleConverter} for the specified locale and Class.
355      *
356      * @param clazz Class for which to remove a registered Converter
357      * @param locale The locale
358      */
359     public void deregister(Class clazz, Locale locale) {
360 
361         lookup(locale).remove(clazz);
362     }
363 
364     /**
365      * Look up and return any registered {@link LocaleConverter} for the specified
366      * destination class and locale; if there is no registered Converter, return
367      * <code>null</code>.
368      *
369      * @param clazz Class for which to return a registered Converter
370      * @param locale The Locale
371      */
372     public LocaleConverter lookup(Class clazz, Locale locale) {
373 
374         LocaleConverter converter = (LocaleConverter) lookup(locale).get(clazz);
375         
376         if (log.isTraceEnabled()) {
377             log.trace("LocaleConverter:" + converter);
378         }
379         
380         return converter;
381     }
382 
383     /**
384      * Look up and return any registered FastHashMap instance for the specified locale;
385      * if there is no registered one, return <code>null</code>.
386      *
387      * @param locale The Locale
388      * @return The FastHashMap instance contains the all {@link LocaleConverter} types for
389      *  the specified locale.
390      */
391     protected FastHashMap lookup(Locale locale) {
392         FastHashMap localeConverters;
393 
394         if (locale == null) {
395             localeConverters = (FastHashMap) mapConverters.get(defaultLocale);
396         }
397         else {
398             localeConverters = (FastHashMap) mapConverters.get(locale);
399 
400             if (localeConverters == null) {
401                 localeConverters = create(locale);
402                 mapConverters.put(locale, localeConverters);
403             }
404         }
405 
406         return localeConverters;
407     }
408 
409     /**
410      *  Create all {@link LocaleConverter} types for specified locale.
411      *
412      * @param locale The Locale
413      * @return The FastHashMap instance contains the all {@link LocaleConverter} types
414      *  for the specified locale.
415      */
416     protected FastHashMap create(Locale locale) {
417 
418         FastHashMap converter = new FastHashMap();
419         converter.setFast(false);
420 
421         converter.put(BigDecimal.class, new BigDecimalLocaleConverter(locale, applyLocalized));
422         converter.put(BigInteger.class, new BigIntegerLocaleConverter(locale, applyLocalized));
423 
424         converter.put(Byte.class, new ByteLocaleConverter(locale, applyLocalized));
425         converter.put(Byte.TYPE, new ByteLocaleConverter(locale, applyLocalized));
426 
427         converter.put(Double.class, new DoubleLocaleConverter(locale, applyLocalized));
428         converter.put(Double.TYPE, new DoubleLocaleConverter(locale, applyLocalized));
429 
430         converter.put(Float.class, new FloatLocaleConverter(locale, applyLocalized));
431         converter.put(Float.TYPE, new FloatLocaleConverter(locale, applyLocalized));
432 
433         converter.put(Integer.class, new IntegerLocaleConverter(locale, applyLocalized));
434         converter.put(Integer.TYPE, new IntegerLocaleConverter(locale, applyLocalized));
435 
436         converter.put(Long.class, new LongLocaleConverter(locale, applyLocalized));
437         converter.put(Long.TYPE, new LongLocaleConverter(locale, applyLocalized));
438 
439         converter.put(Short.class, new ShortLocaleConverter(locale, applyLocalized));
440         converter.put(Short.TYPE, new ShortLocaleConverter(locale, applyLocalized));
441 
442         converter.put(String.class, new StringLocaleConverter(locale, applyLocalized));
443 
444         // conversion format patterns of java.sql.* types should correspond to default
445         // behaviour of toString and valueOf methods of these classes
446         converter.put(java.sql.Date.class, new SqlDateLocaleConverter(locale, "yyyy-MM-dd"));
447         converter.put(java.sql.Time.class, new SqlTimeLocaleConverter(locale, "HH:mm:ss"));
448         converter.put( java.sql.Timestamp.class,
449                        new SqlTimestampLocaleConverter(locale, "yyyy-MM-dd HH:mm:ss.S")
450                      );
451 
452         converter.setFast(true);
453 
454         return converter;
455     }
456 }