1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package javax.faces.convert;
20
21 import java.text.DateFormat;
22 import java.text.ParseException;
23 import java.text.SimpleDateFormat;
24 import java.time.LocalDate;
25 import java.time.LocalDateTime;
26 import java.time.LocalTime;
27 import java.time.OffsetDateTime;
28 import java.time.OffsetTime;
29 import java.time.ZonedDateTime;
30 import java.time.format.DateTimeFormatter;
31 import java.time.format.FormatStyle;
32 import java.time.temporal.TemporalAccessor;
33 import java.time.temporal.TemporalQuery;
34 import java.util.Date;
35 import java.util.Locale;
36 import java.util.TimeZone;
37
38 import javax.faces.component.PartialStateHolder;
39 import javax.faces.component.UIComponent;
40 import javax.faces.context.FacesContext;
41
42 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
43 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
44 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
45
46
47
48
49
50
51
52
53 @JSFConverter(
54 name="f:convertDateTime",
55 bodyContent="empty",
56 tagClass="org.apache.myfaces.taglib.core.ConvertDateTimeTag")
57 @JSFJspProperty(
58 name="binding",
59 returnType = "javax.faces.convert.DateTimeConverter",
60 longDesc = "A ValueExpression that evaluates to a DateTimeConverter.")
61 public class DateTimeConverter
62 implements Converter, PartialStateHolder
63 {
64
65
66 public static final String CONVERTER_ID = "javax.faces.DateTime";
67 public static final String DATE_ID = "javax.faces.converter.DateTimeConverter.DATE";
68 public static final String DATETIME_ID = "javax.faces.converter.DateTimeConverter.DATETIME";
69 public static final String STRING_ID = "javax.faces.converter.STRING";
70 public static final String TIME_ID = "javax.faces.converter.DateTimeConverter.TIME";
71
72
73 private static final String TYPE_DATE = "date";
74 private static final String TYPE_TIME = "time";
75 private static final String TYPE_BOTH = "both";
76 private static final String TYPE_LOCAL_DATE = "localDate";
77 private static final String TYPE_LOCAL_TIME = "localTime";
78 private static final String TYPE_LOCAL_DATE_TIME = "localDateTime";
79 private static final String TYPE_OFFSET_TIME = "offsetTime";
80 private static final String TYPE_OFFSET_DATE_TIME = "offsetDateTime";
81 private static final String TYPE_ZONED_DATE_TIME = "zonedDateTime";
82
83 private static final String STYLE_DEFAULT = "default";
84 private static final String STYLE_MEDIUM = "medium";
85 private static final String STYLE_SHORT = "short";
86 private static final String STYLE_LONG = "long";
87 private static final String STYLE_FULL = "full";
88 private static final TimeZone TIMEZONE_DEFAULT = TimeZone.getTimeZone("GMT");
89
90 private String _dateStyle;
91 private Locale _locale;
92 private String _pattern;
93 private String _timeStyle;
94 private TimeZone _timeZone;
95 private String _type;
96 private boolean _transient;
97
98
99 public DateTimeConverter()
100 {
101 }
102
103
104 public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
105 {
106 if (facesContext == null)
107 {
108 throw new NullPointerException("facesContext");
109 }
110 if (uiComponent == null)
111 {
112 throw new NullPointerException("uiComponent");
113 }
114
115 if (value != null)
116 {
117 value = value.trim();
118 if (value.length() > 0)
119 {
120 if (isJava8DateTimeFormatter())
121 {
122 DateTimeFormatter format = getDateTimeFormatter();
123 try
124 {
125 TemporalQuery tq = getTemporalQuery();
126 if (tq != null)
127 {
128 return format.parse(value, tq);
129 }
130 else
131 {
132 return format.parse(value);
133 }
134 }
135 catch (Exception e)
136 {
137 String type = getType();
138 TemporalAccessor currentDate;
139 if (TYPE_LOCAL_DATE.equals(type) || TYPE_LOCAL_DATE_TIME.equals(type)
140 || TYPE_LOCAL_TIME.equals(type))
141 {
142 currentDate = LocalDateTime.now();
143 }
144 else if (TYPE_OFFSET_TIME.equals(type) || TYPE_OFFSET_DATE_TIME.equals(type))
145 {
146 currentDate = OffsetDateTime.now();
147 }
148 else
149 {
150 currentDate = ZonedDateTime.now();
151 }
152 Object[] args = new Object[]{value,
153 format.format(currentDate),_MessageUtils.getLabel(facesContext, uiComponent)};
154
155 if(type.equals(TYPE_LOCAL_DATE))
156 {
157 throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, DATE_ID, args));
158 }
159 else if (type.equals(TYPE_LOCAL_TIME) || type.equals(TYPE_OFFSET_TIME))
160 {
161 throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, TIME_ID, args));
162 }
163 else if (type.equals(TYPE_LOCAL_DATE_TIME) || type.equals(TYPE_OFFSET_DATE_TIME)
164 || type.equals(TYPE_ZONED_DATE_TIME))
165 {
166 throw new ConverterException(
167 _MessageUtils.getErrorMessage(facesContext, DATETIME_ID, args));
168 }
169 else
170 {
171 throw new ConverterException("invalid type '" + _type + "'");
172 }
173 }
174 }
175 else
176 {
177 DateFormat format = getDateFormat();
178 TimeZone tz = getTimeZone();
179 if( tz != null )
180 {
181 format.setTimeZone(tz);
182 }
183 try
184 {
185 return format.parse(value);
186 }
187 catch (ParseException e)
188 {
189 String type = getType();
190 Object[] args = new Object[]{value,
191 format.format(new Date()),_MessageUtils.getLabel(facesContext, uiComponent)};
192
193 if(type.equals(TYPE_DATE))
194 {
195 throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, DATE_ID, args));
196 }
197 else if (type.equals(TYPE_TIME))
198 {
199 throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, TIME_ID, args));
200 }
201 else if (type.equals(TYPE_BOTH))
202 {
203 throw new ConverterException(
204 _MessageUtils.getErrorMessage(facesContext, DATETIME_ID, args));
205 }
206 else
207 {
208 throw new ConverterException("invalid type '" + _type + "'");
209 }
210 }
211 }
212 }
213 }
214 return null;
215 }
216
217 public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
218 {
219 if (facesContext == null)
220 {
221 throw new NullPointerException("facesContext");
222 }
223 if (uiComponent == null)
224 {
225 throw new NullPointerException("uiComponent");
226 }
227
228 if (value == null)
229 {
230 return "";
231 }
232 if (value instanceof String)
233 {
234 return (String)value;
235 }
236
237 if (isJava8DateTimeFormatter())
238 {
239 DateTimeFormatter format = getDateTimeFormatter();
240
241 if (value instanceof TemporalAccessor)
242 {
243 try
244 {
245 return format.format((TemporalAccessor) value);
246 }
247 catch (Exception e)
248 {
249 throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID,
250 new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
251 }
252 }
253 return null;
254 }
255 else
256 {
257 DateFormat format = getDateFormat();
258 TimeZone tz = getTimeZone();
259 if (tz != null)
260 {
261 format.setTimeZone(tz);
262 }
263 try
264 {
265 return format.format(value);
266 }
267 catch (Exception e)
268 {
269 throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID,
270 new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
271 }
272 }
273 }
274
275 private DateFormat getDateFormat()
276 {
277 String type = getType();
278 DateFormat format;
279 if (_pattern != null)
280 {
281 try
282 {
283 format = new SimpleDateFormat(_pattern, getLocale());
284 }
285 catch (IllegalArgumentException iae)
286 {
287 throw new ConverterException("Invalid pattern", iae);
288 }
289 }
290 else if (type.equals(TYPE_DATE))
291 {
292 format = DateFormat.getDateInstance(calcStyle(getDateStyle()), getLocale());
293 }
294 else if (type.equals(TYPE_TIME))
295 {
296 format = DateFormat.getTimeInstance(calcStyle(getTimeStyle()), getLocale());
297 }
298 else if (type.equals(TYPE_BOTH))
299 {
300 format = DateFormat.getDateTimeInstance(calcStyle(getDateStyle()),
301 calcStyle(getTimeStyle()),
302 getLocale());
303 }
304 else
305 {
306 throw new ConverterException("invalid type '" + _type + "'");
307 }
308
309
310 format.setLenient(false);
311 return format;
312 }
313
314 private DateTimeFormatter getDateTimeFormatter()
315 {
316 DateTimeFormatter formatter = null;
317 String type = getType();
318 String pattern = getPattern();
319 if (pattern != null && pattern.length() > 0)
320 {
321 Locale locale = getLocale();
322 if (locale == null)
323 {
324 formatter = DateTimeFormatter.ofPattern(pattern);
325 }
326 else
327 {
328 formatter = DateTimeFormatter.ofPattern(pattern, locale);
329 }
330 }
331 else
332 {
333 if (TYPE_LOCAL_DATE.equals(type))
334 {
335 formatter = DateTimeFormatter.ofLocalizedDate(calcFormatStyle(getDateStyle()));
336 }
337 else if (TYPE_LOCAL_DATE_TIME.equals(type) )
338 {
339 String timeStyle = getTimeStyle();
340 if (timeStyle != null && timeStyle.length() > 0)
341 {
342 formatter = DateTimeFormatter.ofLocalizedDateTime(
343 calcFormatStyle(getDateStyle()), calcFormatStyle(timeStyle));
344 }
345 else
346 {
347 formatter = DateTimeFormatter.ofLocalizedDateTime(
348 calcFormatStyle(getDateStyle()));
349 }
350 }
351 else if (TYPE_LOCAL_TIME.equals(type) )
352 {
353 formatter = DateTimeFormatter.ofLocalizedTime(calcFormatStyle(getTimeStyle()));
354 }
355 else if (TYPE_OFFSET_TIME.equals(type))
356 {
357 formatter = DateTimeFormatter.ISO_OFFSET_TIME;
358 }
359 else if (TYPE_OFFSET_DATE_TIME.equals(type))
360 {
361 formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
362 }
363 else if (TYPE_ZONED_DATE_TIME.equals(type))
364 {
365 formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
366 }
367
368 Locale locale = getLocale();
369 if (locale != null)
370 {
371 formatter = formatter.withLocale(locale);
372 }
373 }
374 return formatter;
375 }
376
377
378
379
380
381
382
383 private TemporalQuery getTemporalQuery()
384 {
385 String type = getType();
386 if (TYPE_LOCAL_DATE.equals(type))
387 {
388 return LocalDate::from;
389 }
390 else if (TYPE_LOCAL_DATE_TIME.equals(type) )
391 {
392 return LocalDateTime::from;
393 }
394 else if (TYPE_LOCAL_TIME.equals(type) )
395 {
396 return LocalTime::from;
397 }
398 else if (TYPE_OFFSET_TIME.equals(type))
399 {
400 return OffsetTime::from;
401 }
402 else if (TYPE_OFFSET_DATE_TIME.equals(type))
403 {
404 return OffsetDateTime::from;
405 }
406 else if (TYPE_ZONED_DATE_TIME.equals(type))
407 {
408 return ZonedDateTime::from;
409 }
410 return null;
411 }
412
413 private FormatStyle calcFormatStyle(String name)
414 {
415 if (name.equals(STYLE_DEFAULT))
416 {
417 return FormatStyle.MEDIUM;
418 }
419 if (name.equals(STYLE_MEDIUM))
420 {
421 return FormatStyle.MEDIUM;
422 }
423 if (name.equals(STYLE_SHORT))
424 {
425 return FormatStyle.SHORT;
426 }
427 if (name.equals(STYLE_LONG))
428 {
429 return FormatStyle.LONG;
430 }
431 if (name.equals(STYLE_FULL))
432 {
433 return FormatStyle.FULL;
434 }
435
436 throw new ConverterException("invalid style '" + name + "'");
437 }
438
439 private int calcStyle(String name)
440 {
441 if (name.equals(STYLE_DEFAULT))
442 {
443 return DateFormat.DEFAULT;
444 }
445 if (name.equals(STYLE_MEDIUM))
446 {
447 return DateFormat.MEDIUM;
448 }
449 if (name.equals(STYLE_SHORT))
450 {
451 return DateFormat.SHORT;
452 }
453 if (name.equals(STYLE_LONG))
454 {
455 return DateFormat.LONG;
456 }
457 if (name.equals(STYLE_FULL))
458 {
459 return DateFormat.FULL;
460 }
461
462 throw new ConverterException("invalid style '" + name + "'");
463 }
464
465 private boolean isJava8DateTimeFormatter()
466 {
467 String type = getType();
468 if (type != null)
469 {
470 return TYPE_LOCAL_DATE.equals(type) ||
471 TYPE_LOCAL_TIME.equals(type) ||
472 TYPE_LOCAL_DATE_TIME.equals(type) ||
473 TYPE_OFFSET_TIME.equals(type) ||
474 TYPE_OFFSET_DATE_TIME.equals(type) ||
475 TYPE_ZONED_DATE_TIME.equals(type);
476 }
477 else
478 {
479 return false;
480 }
481 }
482
483
484 public void restoreState(FacesContext facesContext, Object state)
485 {
486 if (state != null)
487 {
488 Object[] values = (Object[])state;
489 _dateStyle = (String)values[0];
490 _locale = (Locale)values[1];
491 _pattern = (String)values[2];
492 _timeStyle = (String)values[3];
493 _timeZone = (TimeZone)values[4];
494 _type = (String)values[5];
495 }
496 }
497
498 public Object saveState(FacesContext facesContext)
499 {
500 if (!initialStateMarked())
501 {
502 Object[] values = new Object[6];
503 values[0] = _dateStyle;
504 values[1] = _locale;
505 values[2] = _pattern;
506 values[3] = _timeStyle;
507 values[4] = _timeZone;
508 values[5] = _type;
509 return values;
510 }
511 return null;
512 }
513
514
515
516
517
518
519
520
521 @JSFProperty
522 public String getDateStyle()
523 {
524 return _dateStyle != null ? _dateStyle : STYLE_DEFAULT;
525 }
526
527 public void setDateStyle(String dateStyle)
528 {
529
530 _dateStyle = dateStyle;
531 clearInitialState();
532 }
533
534
535
536
537
538 @JSFProperty
539 public Locale getLocale()
540 {
541 if (_locale != null)
542 {
543 return _locale;
544 }
545 FacesContext context = FacesContext.getCurrentInstance();
546 return context.getViewRoot().getLocale();
547 }
548
549 public void setLocale(Locale locale)
550 {
551 _locale = locale;
552 clearInitialState();
553 }
554
555
556
557
558
559 @JSFProperty
560 public String getPattern()
561 {
562 return _pattern;
563 }
564
565 public void setPattern(String pattern)
566 {
567 _pattern = pattern;
568 clearInitialState();
569 }
570
571
572
573
574
575
576 @JSFProperty
577 public String getTimeStyle()
578 {
579 return _timeStyle != null ? _timeStyle : STYLE_DEFAULT;
580 }
581
582 public void setTimeStyle(String timeStyle)
583 {
584
585 _timeStyle = timeStyle;
586 clearInitialState();
587 }
588
589
590
591
592
593
594
595
596
597 @JSFProperty
598 public TimeZone getTimeZone()
599 {
600 return _timeZone != null ? _timeZone : TIMEZONE_DEFAULT;
601 }
602
603 public void setTimeZone(TimeZone timeZone)
604 {
605 _timeZone = timeZone;
606 clearInitialState();
607 }
608
609 public boolean isTransient()
610 {
611 return _transient;
612 }
613
614 public void setTransient(boolean aTransient)
615 {
616 _transient = aTransient;
617 }
618
619
620
621
622
623
624
625 @JSFProperty
626 public String getType()
627 {
628 return _type != null ? _type : TYPE_DATE;
629 }
630
631 public void setType(String type)
632 {
633
634 _type = type;
635 clearInitialState();
636 }
637
638 private boolean _initialStateMarked = false;
639
640 public void clearInitialState()
641 {
642 _initialStateMarked = false;
643 }
644
645 public boolean initialStateMarked()
646 {
647 return _initialStateMarked;
648 }
649
650 public void markInitialState()
651 {
652 _initialStateMarked = true;
653 }
654 }