1
2
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
38
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
131
132
133
134
135
136
137
138
139
140
141
142
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
171
172
173
174
175
176
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
234
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
262
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
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
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 }