View Javadoc

1   /*
2    * $Id: CalendarTools.java,v 1.18 2011/06/16 09:29:41 jbmeslin Exp $
3    * 
4    */
5   package org.rcfaces.core.internal.tools;
6   
7   import java.text.DateFormat;
8   import java.text.ParseException;
9   import java.text.SimpleDateFormat;
10  import java.util.ArrayList;
11  import java.util.Calendar;
12  import java.util.Date;
13  import java.util.HashMap;
14  import java.util.List;
15  import java.util.Locale;
16  import java.util.Map;
17  import java.util.StringTokenizer;
18  import java.util.TimeZone;
19  
20  import javax.faces.FacesException;
21  import javax.faces.component.UIComponent;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.rcfaces.core.component.capability.IComponentLocaleCapability;
26  import org.rcfaces.core.component.capability.IComponentTimeZoneCapability;
27  import org.rcfaces.core.component.capability.IDateFormatCapability;
28  import org.rcfaces.core.internal.renderkit.AbstractProcessContext;
29  import org.rcfaces.core.internal.renderkit.IComponentRenderContext;
30  import org.rcfaces.core.internal.renderkit.IComponentWriter;
31  import org.rcfaces.core.internal.renderkit.IProcessContext;
32  import org.rcfaces.core.internal.tools.LocaleTools.LocaleDateTimeFormatNormalizer;
33  import org.rcfaces.core.lang.Period;
34  
35  /**
36   * 
37   * @author Olivier Oeuillot (latest modification by $Author: jbmeslin $)
38   * @version $Revision: 1.18 $ $Date: 2011/06/16 09:29:41 $
39   */
40  public class CalendarTools {
41      private static final String REVISION = "$Revision: 1.18 $";
42  
43      private static final Log LOG = LogFactory.getLog(CalendarTools.class);
44  
45      private static final Date EMPTY_DATE[] = new Date[0];
46  
47      private static final Period EMPTY_PERIODS[] = new Period[0];
48  
49      private static final int DEFAULT_CENTURY = 1900;
50  
51      private static final Map DATE_KEYWORDS = new HashMap(4);
52  
53      static {
54          DATE_KEYWORDS.put("now", new IDateKeyword() {
55              private static final String REVISION = "$Revision: 1.18 $";
56  
57              public Date getDate(DateFormat dateFormat) {
58                  return new Date();
59              }
60  
61          });
62          DATE_KEYWORDS.put("today", new DayDateKeyword() {
63              private static final String REVISION = "$Revision: 1.18 $";
64  
65              public Date getDate(DateFormat dateFormat) {
66                  synchronized (dateFormat) {
67                      Calendar calendar = getDayCalendar(dateFormat);
68  
69                      return calendar.getTime();
70                  }
71              }
72          });
73  
74          DATE_KEYWORDS.put("yesterday", new DayDateKeyword() {
75              private static final String REVISION = "$Revision: 1.18 $";
76  
77              public Date getDate(DateFormat dateFormat) {
78                  synchronized (dateFormat) {
79                      Calendar calendar = getDayCalendar(dateFormat);
80  
81                      calendar.add(Calendar.DATE, -1);
82  
83                      return calendar.getTime();
84                  }
85              }
86          });
87          DATE_KEYWORDS.put("tomorrow", new DayDateKeyword() {
88              private static final String REVISION = "$Revision: 1.18 $";
89  
90              public Date getDate(DateFormat dateFormat) {
91                  synchronized (dateFormat) {
92                      Calendar calendar = getDayCalendar(dateFormat);
93  
94                      calendar.add(Calendar.DATE, 1);
95  
96                      return calendar.getTime();
97                  }
98              }
99          });
100     }
101 
102     private static final Map DATE_NORMALIZERS;
103 
104     static {
105         DATE_NORMALIZERS = new HashMap(4);
106         DATE_NORMALIZERS.put("SHORT", new LocaleDateTimeFormatNormalizer(
107                 DateFormat.SHORT));
108         DATE_NORMALIZERS.put("MEDIUM", new LocaleDateTimeFormatNormalizer(
109                 DateFormat.MEDIUM));
110         DATE_NORMALIZERS.put("LONG", new LocaleDateTimeFormatNormalizer(
111                 DateFormat.LONG));
112         DATE_NORMALIZERS.put("FULL", new LocaleDateTimeFormatNormalizer(
113                 DateFormat.FULL));
114     }
115 
116     private static Map TIME_ZONE_BY_COUNTRY = new HashMap();
117     static {
118         addTimeZone("de", "Europe/Berlin");
119         addTimeZone("fr", "Europe/Paris");
120         addTimeZone("gb", "Europe/London");
121         addTimeZone("es", "Europe/Madrid");
122     }
123 
124     private static void addTimeZone(String countryName, String timeZoneId) {
125         TIME_ZONE_BY_COUNTRY.put(countryName.toLowerCase(), TimeZone
126                 .getTimeZone(timeZoneId));
127     }
128 
129     /*
130      * public static void setDate(AbstractCalendarComponent component, String
131      * date) { DateFormat dateFormat = getShortDateFormat(component, xxx);
132      * 
133      * Date d = parseDate(dateFormat, date);
134      * 
135      * component.setValue(d); }
136      * 
137      * public static void setPeriod(AbstractCalendarComponent component, String
138      * period) { DateFormat dateFormat = getShortDateFormat(component, xxx);
139      * 
140      * Date ds[] = parsePeriod(dateFormat, period);
141      * 
142      * component.setValue(ds); }
143      */
144 
145     private static Period parsePeriod(DateFormat dateFormat, String dates) {
146         StringTokenizer st = new StringTokenizer(dates, ":");
147         int cnt = st.countTokens();
148         if (cnt == 0) {
149             return null;
150         }
151 
152         Date d = parseDate(dateFormat, st.nextToken());
153         if (d == null) {
154             return null;
155         }
156 
157         if (cnt == 1) {
158             return new Period(d, d);
159         }
160 
161         Date d2 = parseDate(dateFormat, st.nextToken());
162         if (d2 == null) {
163             return null;
164         }
165 
166         return new Period(d, d2);
167     }
168 
169     /*
170      * private static Date computeNext(FacesContext facesContext, UIComponent
171      * calendarComponent, Date date) { Calendar calendar =
172      * getAttributesCalendar(facesContext, calendarComponent);
173      * 
174      * calendar.setTime(date); calendar.add(Calendar.DATE, 1);
175      * 
176      * return calendar.getTime(); }
177      */
178 
179     private static Period[] parsePeriods(DateFormat dateFormat, String dates) {
180         List l = null;
181         StringTokenizer st = new StringTokenizer(dates, ",");
182         for (; st.hasMoreTokens();) {
183             String token = st.nextToken();
184 
185             Period ds = parsePeriod(dateFormat, token);
186             if (ds == null) {
187                 continue;
188             }
189 
190             if (l == null) {
191                 l = new ArrayList();
192             }
193 
194             l.add(ds);
195         }
196 
197         Period ds[] = EMPTY_PERIODS;
198         if (l != null) {
199             ds = (Period[]) l.toArray(new Period[l.size()]);
200         }
201 
202         return ds;
203     }
204 
205     private static Date[] parseDates(DateFormat dateFormat, String dates) {
206         List l = null;
207         StringTokenizer st = new StringTokenizer(dates, ",");
208         for (; st.hasMoreTokens();) {
209             String token = st.nextToken();
210 
211             Date ds = parseDate(dateFormat, token);
212             if (ds == null) {
213                 continue;
214             }
215 
216             if (l == null) {
217                 l = new ArrayList();
218             }
219 
220             l.add(ds);
221         }
222 
223         Date ds[] = EMPTY_DATE;
224         if (l != null) {
225             ds = (Date[]) l.toArray(new Date[l.size()]);
226         }
227 
228         return ds;
229     }
230 
231     /*
232      * 
233      * public static final Date parseDate(FacesContext facesContext, UIComponent
234      * calendarComponent, String date) { }
235      */
236 
237     private static final Date parseDate(DateFormat dateFormat, String date) {
238 
239         IDateKeyword dateKeyword = (IDateKeyword) DATE_KEYWORDS.get(date
240                 .toLowerCase());
241         if (dateKeyword != null) {
242             return dateKeyword.getDate(dateFormat);
243         }
244 
245         try {
246             synchronized (dateFormat) {
247                 return dateFormat.parse(date);
248             }
249 
250         } catch (ParseException e) {
251             throw new FacesException("Can not parse date '" + date + "'.", e);
252         }
253     }
254 
255     private static interface IDateKeyword {
256         Date getDate(DateFormat dateFormat);
257     }
258 
259     /**
260      * 
261      * @author Olivier Oeuillot (latest modification by $Author: jbmeslin $)
262      * @version $Revision: 1.18 $ $Date: 2011/06/16 09:29:41 $
263      */
264     private static abstract class DayDateKeyword implements IDateKeyword {
265         private static final String REVISION = "$Revision: 1.18 $";
266 
267         protected final Calendar getDayCalendar(DateFormat dateFormat) {
268 
269             Calendar calendar = dateFormat.getCalendar();
270             calendar.setTime(new Date());
271 
272             calendar.set(Calendar.MILLISECOND, 0);
273             calendar.set(Calendar.SECOND, 0);
274             calendar.set(Calendar.MINUTE, 0);
275             calendar.set(Calendar.HOUR_OF_DAY, 0);
276 
277             return calendar;
278         }
279 
280     }
281 
282     public static Object parseValue(IProcessContext processContext,
283             UIComponent component, String value, boolean literalValue) {
284 
285         DateFormat dateFormat = getDateFormat(processContext, component,
286                 literalValue);
287 
288         if (value.indexOf(':') >= 0) {
289             return parsePeriods(dateFormat, value);
290         }
291 
292         if (value.indexOf(',') >= 0) {
293             return parseDates(dateFormat, value);
294         }
295 
296         return parseDate(dateFormat, value);
297     }
298 
299     public static Date parseDate(IProcessContext processContext,
300             UIComponent component, String value, boolean literalValue) {
301 
302         DateFormat dateFormat = getDateFormat(processContext, component,
303                 literalValue);
304 
305         return parseDate(dateFormat, value);
306     }
307 
308     public static String formatDate(UIComponent calendarComponent, Date date,
309             boolean literalValue) {
310         DateFormat dateFormat = getDateFormat(null, calendarComponent,
311                 literalValue);
312         synchronized (dateFormat) {
313             return dateFormat.format(date);
314         }
315     }
316 
317     public static Date parseTwoDigitYearDate(UIComponent component,
318             String value, boolean literalValue) {
319         if (value == null || value.length() < 1) {
320             return null;
321         }
322 
323         boolean onlyDigit = true;
324         char chs[] = value.toCharArray();
325         for (int i = 0; i < chs.length; i++) {
326             if (Character.isDigit(chs[i])) {
327                 continue;
328             }
329 
330             onlyDigit = false;
331             break;
332         }
333 
334         if (onlyDigit == false) {
335             DateFormat dateFormat = getDateFormat(null, component, literalValue);
336 
337             return parseDate(dateFormat, value);
338         }
339 
340         // Il n'y a que l'année de specifier !
341 
342         Calendar calendar = getCalendar(null, component, literalValue);
343 
344         int year = Integer.parseInt(value);
345         if (year < 1000) {
346             throw new FacesException(
347                     "You must specify two digit year attribute with value more than 1000.");
348         }
349 
350         calendar.set(year, 0, 1);
351         return calendar.getTime();
352     }
353 
354     private static DateFormat getDateFormat(IProcessContext processContext,
355             UIComponent component, boolean literalValue) {
356 
357         DateFormat dateFormat = null;
358         if (component instanceof IDateFormatCapability) {
359             String dateFormatString = ((IDateFormatCapability) component)
360                     .getDateFormat();
361 
362             if (dateFormatString != null) {
363                 Locale locale = LocaleTools.getLocale(component, literalValue);
364 
365                 LocaleDateTimeFormatNormalizer formatNormalizer = (LocaleDateTimeFormatNormalizer) DATE_NORMALIZERS
366                         .get(dateFormatString.toUpperCase());
367 
368                 if (formatNormalizer != null) {
369                     return formatNormalizer.getDateFormat(locale);
370                 }
371 
372                 try {
373                     dateFormat = new SimpleDateFormat(dateFormatString, locale);
374 
375                 } catch (IllegalArgumentException ex) {
376                     throw new FacesException(
377                             "Illegal simple date format pattern '"
378                                     + dateFormatString + "' for component '"
379                                     + component.getId() + "'", ex);
380                 }
381             }
382         }
383 
384         if (dateFormat == null) {
385             dateFormat = (DateFormat) LocaleTools.getDefaultFormat(component,
386                     LocaleTools.DATE_TYPE, literalValue);
387         }
388 
389         TimeZone timeZone = getCalendar(processContext, component, literalValue)
390                 .getTimeZone();
391         if (dateFormat.getCalendar().getTimeZone().equals(timeZone) == false) {
392             synchronized (dateFormat) {
393                 dateFormat = (DateFormat) dateFormat.clone();
394             }
395 
396             dateFormat.setTimeZone(timeZone);
397         }
398 
399         return dateFormat;
400     }
401 
402     public static String getDateFormatPattern(Locale locale, int style) {
403         return LocaleTools.getFormatPattern(locale, style,
404                 LocaleTools.DATE_TYPE);
405     }
406 
407     public static String normalizeFormat(
408             IComponentRenderContext componentRenderContext, String format) {
409         return LocaleTools.normalizeFormat(componentRenderContext, format,
410                 LocaleTools.DATE_TYPE, DATE_NORMALIZERS);
411     }
412 
413     public static String getDefaultPattern(Locale locale) {
414         return LocaleTools.getDefaultPattern(locale, LocaleTools.DATE_TYPE);
415     }
416 
417     public static Calendar getCalendar(IComponentWriter writer) {
418         IComponentRenderContext componentRenderContext = writer
419                 .getComponentRenderContext();
420 
421         return getCalendar(componentRenderContext.getRenderContext()
422                 .getProcessContext(), componentRenderContext.getComponent(),
423                 false);
424     }
425 
426     public static Calendar getCalendar(IProcessContext processContext,
427             UIComponent component, boolean literalValue) {
428         Locale locale = null;
429         TimeZone timeZone = null;
430 
431         if (literalValue) {
432             if (processContext == null) {
433                 processContext = AbstractProcessContext.getProcessContext(null);
434             }
435 
436             locale = PageConfiguration.getLiteralLocale(processContext,
437                     component);
438             timeZone = PageConfiguration.getLiteralTimeZone(processContext,
439                     component);
440 
441         } else {
442 
443             if (component instanceof IComponentLocaleCapability) {
444                 locale = ((IComponentLocaleCapability) component)
445                         .getComponentLocale();
446             }
447 
448             if (component instanceof IComponentTimeZoneCapability) {
449                 timeZone = ((IComponentTimeZoneCapability) component)
450                         .getComponentTimeZone();
451             }
452 
453             if (locale == null && timeZone == null) {
454 
455                 if (LOG.isDebugEnabled()) {
456                     LOG.debug("Component[" + component.getId()
457                             + "] => returns user calendar !");
458                 }
459 
460                 if (processContext == null) {
461                     processContext = AbstractProcessContext
462                             .getProcessContext(null);
463                 }
464 
465                 return processContext.getUserCalendar();
466             }
467 
468             if (locale == null) {
469                 if (processContext == null) {
470                     processContext = AbstractProcessContext
471                             .getProcessContext(null);
472                 }
473 
474                 if (processContext != null) {
475                     locale = processContext.getUserLocale();
476                 }
477             }
478 
479             if (timeZone == null) {
480                 if (processContext == null) {
481                     processContext = AbstractProcessContext
482                             .getProcessContext(null);
483                 }
484 
485                 if (processContext != null) {
486                     timeZone = processContext.getUserTimeZone();
487                 }
488             }
489 
490             if (LOG.isDebugEnabled()) {
491                 LOG.debug("Component[" + component.getId() + "] literal="
492                         + literalValue + " => Locale=" + locale + " timeZone="
493                         + timeZone);
494             }
495         }
496 
497         if (locale != null && timeZone == null) {
498             // Il faut trouver la timeZone associée au locale
499             String country = locale.getCountry();
500             if (country != null) {
501                 timeZone = getTimeZoneFromCountry(country);
502             }
503 
504             if (timeZone == null) {
505                 timeZone = processContext.getDefaultTimeZone();
506             }
507         }
508 
509         if (locale != null && timeZone != null) {
510             return Calendar.getInstance(timeZone, locale);
511         }
512 
513         if (locale != null) {
514             return Calendar.getInstance(locale);
515         }
516 
517         if (timeZone != null) {
518             return Calendar.getInstance(timeZone);
519         }
520 
521         return Calendar.getInstance();
522     }
523 
524     private static TimeZone getTimeZoneFromCountry(String country) {
525         country = country.toLowerCase();
526 
527         return (TimeZone) TIME_ZONE_BY_COUNTRY.get(country);
528     }
529 }