View Javadoc

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