View Javadoc

1   /*
2    * $Id: LocaleTools.java,v 1.7 2008/12/09 16:33:53 oeuillot Exp $
3    */
4   package org.rcfaces.core.internal.tools;
5   
6   import java.text.DateFormat;
7   import java.text.DecimalFormat;
8   import java.text.Format;
9   import java.text.NumberFormat;
10  import java.text.SimpleDateFormat;
11  import java.util.HashMap;
12  import java.util.Locale;
13  import java.util.Map;
14  import java.util.TimeZone;
15  
16  import javax.faces.FacesException;
17  import javax.faces.component.UIComponent;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.rcfaces.core.component.capability.IComponentLocaleCapability;
22  import org.rcfaces.core.internal.Constants;
23  import org.rcfaces.core.internal.converter.LocaleConverter;
24  import org.rcfaces.core.internal.renderkit.IComponentRenderContext;
25  
26  /**
27   * 
28   * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
29   * @version $Revision: 1.7 $ $Date: 2008/12/09 16:33:53 $
30   */
31  public class LocaleTools {
32      private static final String REVISION = "$Revision: 1.7 $";
33  
34      private static final Log LOG = LogFactory.getLog(LocaleTools.class);
35  
36      public static final boolean NORMALIZE_LOCALE_PARAMETER_SUPPORT = false;
37  
38      public static final int DATE_TYPE = 0;
39  
40      public static final int TIME_TYPE = 1;
41  
42      public static final int DATE_TIME_TYPE = 2;
43  
44      public static final int NUMBER_TYPE = 3;
45  
46      public static final int INTEGER_TYPE = 4;
47  
48      public static final int PERCENT_TYPE = 5;
49  
50      public static final int CURRENCY_TYPE = 6;
51  
52      public static final int MAX_TYPE = 6;
53  
54      private static final Map dateFormatByLocale;
55      static {
56          if (Constants.CACHED_LOCALE_FORMATS) {
57              dateFormatByLocale = new HashMap(32);
58          }
59      }
60  
61      private static final int DEFAULT_STYLE_BY_TYPE[] = new int[MAX_TYPE + 1];
62      static {
63          DEFAULT_STYLE_BY_TYPE[0] = DateFormat.SHORT;
64          DEFAULT_STYLE_BY_TYPE[1] = DateFormat.MEDIUM;
65          DEFAULT_STYLE_BY_TYPE[2] = DateFormat.MEDIUM;
66      }
67  
68      /**
69       * 
70       * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
71       * @version $Revision: 1.7 $ $Date: 2008/12/09 16:33:53 $
72       */
73      protected static interface IFormatNormalizer {
74          String normalizeFormat(IComponentRenderContext componentRenderContext,
75                  int type, String format, String param);
76      }
77  
78      /**
79       * 
80       * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
81       * @version $Revision: 1.7 $ $Date: 2008/12/09 16:33:53 $
82       */
83      protected static class LocaleDateTimeFormatNormalizer implements
84              LocaleTools.IFormatNormalizer {
85          private static final String REVISION = "$Revision: 1.7 $";
86  
87          private final int style;
88  
89          private final Map dateFormatByLocale = new HashMap();
90  
91          LocaleDateTimeFormatNormalizer(int style) {
92              this.style = style;
93          }
94  
95          public String normalizeFormat(
96                  IComponentRenderContext componentRenderContext, int type,
97                  String format, String param) {
98  
99              Locale locale = getLocale(componentRenderContext, param);
100 
101             return getFormatPattern(locale, style, type);
102         }
103 
104         protected Locale getLocale(
105                 IComponentRenderContext componentRenderContext, String param) {
106             Locale locale = null;
107 
108             if (param != null) {
109                 locale = (Locale) LocaleConverter.SINGLETON.getAsObject(null,
110                         null, param);
111                 if (locale == null) {
112                     throw new FacesException("Invalid locale name '" + param
113                             + "'.");
114                 }
115             }
116 
117             if (locale == null) {
118                 locale = componentRenderContext.getRenderContext()
119                         .getProcessContext().getUserLocale();
120             }
121 
122             return locale;
123         }
124 
125         public DateFormat getDateFormat(Locale locale) {
126             DateFormat dateFormat;
127             synchronized (dateFormatByLocale) {
128                 dateFormat = (DateFormat) dateFormatByLocale.get(locale);
129                 if (dateFormat == null) {
130                     dateFormat = DateFormat.getDateInstance(style, locale);
131                     dateFormatByLocale.put(locale, dateFormat);
132                 }
133             }
134             return dateFormat;
135         }
136 
137     }
138 
139     public static String normalizeFormat(
140             IComponentRenderContext componentRenderContext, String format,
141             int type, Map normalizers) {
142         if (format == null || format.length() < 1) {
143             return format;
144         }
145 
146         String param = null;
147         if (LocaleTools.NORMALIZE_LOCALE_PARAMETER_SUPPORT) {
148             if (format.charAt(0) != '$') {
149                 return format;
150             }
151 
152             format = format.substring(1);
153 
154             int idx = format.indexOf('(');
155             if (idx >= 0) {
156                 param = format.substring(idx + 1);
157                 format = format.substring(0, idx);
158 
159                 idx = param.lastIndexOf(')');
160                 if (idx < 0 || idx != param.length() - 1) {
161                     throw new FacesException("Invalid date format '" + format
162                             + "' parentheses are not correctly balanced.");
163                 }
164 
165                 param = param.substring(0, idx);
166             }
167         }
168 
169         LocaleTools.IFormatNormalizer normalizer = (LocaleTools.IFormatNormalizer) normalizers
170                 .get(format.toUpperCase());
171         if (normalizer == null) {
172             return format;
173         }
174 
175         return normalizer.normalizeFormat(componentRenderContext, type, format,
176                 param);
177     }
178 
179     private static final CachedLocale getCachedLocale(Locale locale) {
180         synchronized (dateFormatByLocale) {
181             CachedLocale cachedLocale = (CachedLocale) dateFormatByLocale
182                     .get(locale);
183             if (cachedLocale != null) {
184                 return cachedLocale;
185             }
186 
187             cachedLocale = new CachedLocale(locale);
188             dateFormatByLocale.put(locale, cachedLocale);
189 
190             return cachedLocale;
191         }
192     }
193 
194     private static Format getFormatByType(Locale locale, TimeZone timeZone,
195             int type, int style) {
196         if (style < 0) {
197             style = DEFAULT_STYLE_BY_TYPE[type];
198         }
199 
200         Format format;
201         switch (type) {
202         case DATE_TYPE:
203             format = DateFormat.getDateInstance(style, locale);
204             break;
205 
206         case TIME_TYPE:
207             format = DateFormat.getTimeInstance(style, locale);
208             break;
209 
210         case DATE_TIME_TYPE:
211             format = DateFormat.getDateTimeInstance(style, style, locale);
212             break;
213 
214         case NUMBER_TYPE:
215             return NumberFormat.getNumberInstance(locale);
216 
217         case INTEGER_TYPE:
218             return NumberFormat.getIntegerInstance(locale);
219 
220         case PERCENT_TYPE:
221             return NumberFormat.getPercentInstance(locale);
222 
223         case CURRENCY_TYPE:
224             return NumberFormat.getCurrencyInstance(locale);
225 
226         default:
227             LOG.error("Invalid format type=" + type);
228             return null;
229         }
230 
231         if (timeZone != null && (format instanceof DateFormat)) {
232             DateFormat dateFormat = (DateFormat) format;
233 
234             if (dateFormat.getCalendar().getTimeZone().equals(timeZone) == false) {
235                 dateFormat.setTimeZone(timeZone);
236             }
237         }
238 
239         return format;
240     }
241 
242     private static String getPattern(Format format) {
243         if (format instanceof SimpleDateFormat) {
244             return ((SimpleDateFormat) format).toPattern();
245         }
246 
247         if (format instanceof DecimalFormat) {
248             return ((DecimalFormat) format).toPattern();
249         }
250 
251         throw new FacesException("Can not get format pattern from format: "
252                 + format);
253 
254     }
255 
256     /**
257      * 
258      * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
259      * @version $Revision: 1.7 $ $Date: 2008/12/09 16:33:53 $
260      */
261     private static final class CachedLocale {
262         private final Locale locale;
263 
264         private final Format defaultFormats[];
265 
266         private String patternsByType[][];
267 
268         public CachedLocale(Locale locale) {
269             this.locale = locale;
270 
271             this.defaultFormats = new Format[MAX_TYPE + 1];
272         }
273 
274         public Locale getLocale() {
275             return locale;
276         }
277 
278         public Format getDefaultFormat(int type) {
279             Format format;
280 
281             synchronized (defaultFormats) {
282                 format = defaultFormats[type];
283                 if (format == null) {
284                     format = getFormatByType(locale, null, type, -1);
285                     defaultFormats[type] = format;
286                 }
287             }
288 
289             return format;
290         }
291 
292         public String getDefaultPattern(int type) {
293             return getPattern(type, DEFAULT_STYLE_BY_TYPE[type]);
294         }
295 
296         public String getPattern(int type, int style) {
297 
298             synchronized (this) {
299                 if (patternsByType == null) {
300                     patternsByType = new String[MAX_TYPE + 1][];
301                 }
302 
303                 String patterns[] = patternsByType[type];
304                 if (patterns != null) {
305                     String pattern = patterns[style];
306                     if (pattern != null) {
307                         return pattern;
308                     }
309                 } else {
310                     patterns = new String[DateFormat.SHORT + 1];
311                     patternsByType[type] = patterns;
312                 }
313 
314                 boolean dateFormat = (type == DATE_TYPE);
315                 if (dateFormat && style == DateFormat.MEDIUM) {
316                     // Remplace les 2 yy par 4 yyyy
317                     // On retourne au format SHORT pour transformer ensuite le
318                     // yy en yyyy
319                     style = DateFormat.SHORT;
320                 }
321 
322                 Format format;
323                 if (style == DEFAULT_STYLE_BY_TYPE[type]) {
324                     format = getDefaultFormat(type);
325 
326                 } else {
327                     format = getFormatByType(locale, null, type, style);
328                 }
329 
330                 String pattern;
331                 synchronized (format) {
332                     pattern = LocaleTools.getPattern(format);
333                 }
334 
335                 if (dateFormat) {
336                     // Remplace les 2 yy en 4 yyyy
337                     if (pattern.indexOf("yyy") < 0) {
338                         int idx = pattern.indexOf("yy");
339                         if (idx >= 0) {
340                             pattern = pattern.substring(0, idx) + "yy"
341                                     + pattern.substring(idx);
342                         }
343                     }
344                 }
345 
346                 patterns[style] = pattern;
347 
348                 return pattern;
349             }
350         }
351     }
352 
353     public static String getFormatPattern(Locale locale, int style, int type) {
354 
355         if (Constants.CACHED_LOCALE_FORMATS == false) {
356             Format format = getFormatByType(locale, null, type, style);
357 
358             return getPattern(format);
359         }
360 
361         return LocaleTools.getCachedLocale(locale).getPattern(type, style);
362     }
363 
364     public static Locale getLocale(UIComponent component, boolean literalValue) {
365         if (literalValue) {
366             return PageConfiguration.getLiteralLocale(null, component);
367         }
368 
369         if (component instanceof IComponentLocaleCapability) {
370             Locale locale = ((IComponentLocaleCapability) component)
371                     .getComponentLocale();
372 
373             if (locale != null) {
374                 return locale;
375             }
376         }
377 
378         return ContextTools.getUserLocale(null);
379     }
380 
381     public static Format getDefaultFormat(UIComponent component, int type,
382             boolean literalValue) {
383 
384         Locale locale = getLocale(component, literalValue);
385 
386         if (Constants.CACHED_LOCALE_FORMATS == false) {
387             return getFormatByType(locale, null, type, -1);
388         }
389 
390         return LocaleTools.getCachedLocale(locale).getDefaultFormat(type);
391     }
392 
393     public static String getDefaultPattern(Locale locale, int type) {
394 
395         if (Constants.CACHED_LOCALE_FORMATS == false) {
396             Format format = getFormatByType(locale, null, type, -1);
397 
398             return getPattern(format);
399         }
400 
401         return LocaleTools.getCachedLocale(locale).getDefaultPattern(type);
402     }
403 
404 }