1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.convert;
20
21 import java.text.DateFormat;
22 import java.text.DateFormatSymbols;
23 import java.text.ParseException;
24 import java.text.SimpleDateFormat;
25
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Calendar;
29 import java.util.Date;
30 import java.util.GregorianCalendar;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.MissingResourceException;
36 import java.util.ResourceBundle;
37 import java.util.TimeZone;
38
39 import javax.el.ValueExpression;
40
41 import javax.faces.application.FacesMessage;
42 import javax.faces.component.StateHolder;
43 import javax.faces.component.UIComponent;
44 import javax.faces.component.ValueHolder;
45 import javax.faces.context.FacesContext;
46 import javax.faces.convert.Converter;
47 import javax.faces.convert.ConverterException;
48 import javax.faces.el.ValueBinding;
49
50 import org.apache.myfaces.trinidad.bean.FacesBean;
51 import org.apache.myfaces.trinidad.bean.PropertyKey;
52 import org.apache.myfaces.trinidad.context.RequestContext;
53 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
54 import org.apache.myfaces.trinidad.util.ComponentUtils;
55 import org.apache.myfaces.trinidad.util.MessageFactory;
56
57
58 /**
59 * <p>{@link Converter} implementation for <code>java.util.Date</code>
60 * values. Converts an strings to and from java.util.Date objects.</p>
61 *
62 * The converter has additonal features than standard JSF
63 * {@link javax.faces.convert.DateTimeConverter}.
64 *
65 * New dateStyle <code>shortish</code> has been introduced. Shortish is identical
66 * to <code>short</code> but forces the year to be a full four digits.
67 * If dateStyle is not set, then <code>dateStyle</code> defaults to
68 * <code>shortish</code>.
69 *
70 * <p>Timezone can be set per web-app in trinidad-config.xml configuration file.
71 * If <code>timeZone</code> is not set on the converter, then timezone will be defaulted to the
72 * value set in trinidad-config.xml configuration file. If it is not set in the
73 * configuration file, then it will be defaulted to GMT.</p>
74 *
75 * <p>The converter always allows a level of <code>leniency</code> while converting
76 * user input value to date to the following extent.
77 * <ul>
78 * <li>A converter with associated pattern 'MMM' for month, when attached to any
79 * value holder, will accept values with month specified in the form 'MM' or 'M'
80 * as valid.</li>
81 * <li>Allows use of separators '-' or '.' or '/' irrespective of the separator
82 * specified in the associated pattern.</li>
83 * <li>The leniency is applicable to both 'pattern' and 'secondaryPattern'.</li>
84 * </ul></p>
85 * <p>
86 * For example:</br>
87 * When pattern on the converter is set to "MMM/d/yyyy" the following inputs
88 * are tolerated as valid by the converter.</br>
89 * <dl>
90 * <dt>Jan/4/2004</dt>
91 * <dt>Jan-4-2004</dt>
92 * <dt>Jan.4.2004</dt>
93 * <dt>01/4/2004</dt>
94 * <dt>01-4-2004</dt>
95 * <dt>01.4.2004</dt>
96 * <dt>1/4/2004</dt>
97 * <dt>1-4-2004</dt>
98 * <dt>1.4.2004</dt>
99 * </dl>
100 * </p>
101 *
102 * The detail part of faces message,for conversion errors can be customized
103 * by overriding the message associated with each CONVERT_[TYPE]_MESSAGE_ID.
104 *
105 * <p>The methods used for customizing the detail message associated with each id
106 * is given below:</p>
107 * <ol>
108 * <li>{@link #CONVERT_DATE_MESSAGE_ID} - {@link #setMessageDetailConvertDate(String)}</li>
109 * <li>{@link #CONVERT_TIME_MESSAGE_ID} - {@link #setMessageDetailConvertTime(String) }</li>
110 * <li>{@link #CONVERT_BOTH_MESSAGE_ID} - {@link #setMessageDetailConvertBoth(String) }</li>
111 * </ol> The custom messages can contain placeholders, which will be replaced with
112 * values as specified in its corresponding message id.
113 *
114 * <p>The <code>getAsObject()</code> method parses a String into a
115 * <code>java.util.Date</code>, according to the following algorithm:</p>
116 * <ul>
117 * <li>If the specified String is null, return
118 * a <code>null</code>. Otherwise, trim leading and trailing
119 * whitespace before proceeding.</li>
120 * <li>If the specified String - after trimming - has a zero length,
121 * return <code>null</code>.</li>
122 * <li>If the <code>locale</code> property is not null,
123 * use that <code>Locale</code> for managing parsing. Otherwise, use the
124 * <code>Locale</code> from the <code>UIViewRoot</code>.</li>
125 * <li>If a <code>pattern</code> has been specified, its syntax must conform
126 * the rules specified by <code>java.text.SimpleDateFormat</code>. Such
127 * a pattern will be used to parse, and the <code>type</code>,
128 * <code>dateStyle</code>, and <code>timeStyle</code> properties
129 * will be ignored.</li>
130 * <li>If a <code>pattern</code> has not been specified, parsing will be based
131 * on the <code>type</code> property, which expects a date value, a time
132 * value, or both. Any date and time values included will be parsed in
133 * accordance to the styles specified by <code>dateStyle</code> and
134 * <code>timeStyle</code>, respectively.</li>
135 * <li>If conversion fails with <code>pattern</code> or <code>style</code>
136 * and if <code> secondaryPattern</code> is set, re parsers based on the
137 * <code>secondaryPattern</code>. Syntax for <code>secondaryPattern</code>
138 * must conform to the rules specified by
139 * <code>java.text.SimpleDateFormat</code>.</li>
140 * <li>Parsing is lenient as outlined earlier and is not the same as setting
141 * leniency on <code>java.text.DateFormat</code>
142 * </ul>
143 *
144 * <p>The <code>getAsString()</code> method expects a value of type
145 * <code>java.util.Date</code> (or a subclass), and creates a formatted
146 * String according to the following algorithm:</p>
147 * <ul>
148 * <li>If the specified value is null, return a zero-length String.</li>
149 * <li>If the specified value is a String, return it unmodified.</li>
150 * <li>If the <code>locale</code> property is not null,
151 * use that <code>Locale</code> for managing formatting. Otherwise, use the
152 * <code>Locale</code> from the <code>UIViewRoot</code>.</li>
153 * <li>If a <code>pattern</code> has been specified, its syntax must conform
154 * the rules specified by <code>java.text.SimpleDateFormat</code>. Such
155 * a pattern will be used to format, and the <code>type</code>,
156 * <code>dateStyle</code>, and <code>timeStyle</code> properties
157 * will be ignored.</li>
158 * <li>If a <code>pattern</code> has not been specified, formatting will be
159 * based on the <code>type</code> property, which includes a date value,
160 * a time value, or both into the formatted String. Any date and time
161 * values included will be formatted in accordance to the styles specified
162 * by <code>dateStyle</code> and <code>timeStyle</code>, respectively.</li>
163 * <li><code>secondaryPattern</code> even if set is never used for formatting
164 * to a String</li>
165 * </ul>
166 *
167 * @see #CONVERT_DATE_MESSAGE_ID
168 * @see #CONVERT_TIME_MESSAGE_ID
169 * @see #CONVERT_BOTH_MESSAGE_ID
170 * @see java.text.DateFormat
171 * @see java.text.SimpleDateFormat
172 *
173 * @version $Name: $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/convert/DateTimeConverter.java#0 $) $Date: 10-nov-2005.19:09:11 $
174 */
175 public class DateTimeConverter extends javax.faces.convert.DateTimeConverter
176 implements Converter, StateHolder
177
178 {
179
180 /**
181 * <p>Standard converter id for this converter.</p>
182 */
183 public static final String CONVERTER_ID = "org.apache.myfaces.trinidad.DateTime";
184
185 /**
186 * <p>The message identifier of the FacesMessage to be created if
187 * the value cannot be converted to a date, when <code>pattern</code>
188 * is null or not set and <code>type</code> is set to <code>'date'</code>.
189 * Or when failures occurs when value cannot be converted to a date
190 * based on the pattern set. The message format string for this message
191 * may optionally include <code>{0}</code>, <code>{1}</code>, <code>{4}</code>
192 * placeholdes, which will be replaced by input value, component label
193 * and example date based on the <code>pattern</code> or based on the
194 * <code>style</code> when <code>type</code> is set to <code>'date'</code>.</p>
195 */
196 public static final String CONVERT_DATE_MESSAGE_ID =
197 "org.apache.myfaces.trinidad.convert.DateTimeConverter.CONVERT_DATE";
198
199 /**
200 * <p>The message identifier of the FacesMessage to be created if
201 * the value cannot be converted date time object, when <code>type</code>
202 * is set to <code>'time'</code> and pattern is null or not set.
203 * The message format string for this message may optionally include
204 * <code>{0}</code>, <code>{1}</code>, <code>{4}</code>
205 * placeholdes, which will be replaced by input value, component label
206 * and a time example, based on the <code>timeStyle</code>
207 * set in the converter.</p>
208 */
209 public static final String CONVERT_TIME_MESSAGE_ID =
210 "org.apache.myfaces.trinidad.convert.DateTimeConverter.CONVERT_TIME";
211
212 /**
213 * <p>The message identifier of the FacesMessage to be created if
214 * the value cannot be converted to a date when <code>type</code>
215 * is set to <code>'both'</code> and pattern is either null or not set. The
216 * message format string for this message may optionally include
217 * <code>{0}</code>, <code>{1}</code>, <code>{4}</code>
218 * placeholdes, which will be replaced by input value, component label
219 * and a date-time example, based on the <code>dateStyle</code> and
220 * <code>timeStyle</code> set in the converter.</p>
221 */
222 public static final String CONVERT_BOTH_MESSAGE_ID =
223 "org.apache.myfaces.trinidad.convert.DateTimeConverter.CONVERT_BOTH";
224
225 /**
226 * Creates a DateTimeConverter
227 */
228 public DateTimeConverter()
229 {
230 }
231
232 /**
233 * Creates a DateTimeConverter with the specified SimpleDateFormat format
234 * pattern
235 * @param pattern a primary pattern; this will be used to format
236 * and parser strings.
237 */
238 public DateTimeConverter(String pattern)
239 {
240 this();
241 setPattern(pattern);
242 }
243
244 /**
245 * Creates a DateTimeConverter with the specified SimpleDateFormat format
246 * pattern and a secondary pattern.
247 * @param pattern a primary pattern; this will be used to format
248 * and parser strings.
249 * @param secondaryPattern a second pattern, which will be used
250 * as a second attempt to parse a string if the primary pattern or
251 * styles fail, but is never used for formatting strings.
252 */
253 public DateTimeConverter(String pattern, String secondaryPattern)
254 {
255 this(pattern);
256 setSecondaryPattern(secondaryPattern);
257
258 }
259
260 /**
261 * <p>Convert the specified string value, which is associated with
262 * the specified {@link UIComponent}, into a java.util.Date object
263 * based on the values set.</p>
264 *
265 * @param context {@link FacesContext} for the request being processed
266 * @param component {@link UIComponent} with which this model object
267 * value is associated
268 * @param value String value to be converted (may be <code>null</code>)
269 *
270 * @return <code>null</code> if the value to convert is <code>null</code>,
271 * otherwise return a java.util.Date object.
272 *
273 * @exception ConverterException if conversion cannot be successfully
274 * performed
275 * @exception NullPointerException if <code>context</code> or
276 * <code>component</code> is <code>null</code>
277 *
278 * @exception IllegalArgumentException if the <code>value</code> is of
279 * type other than {@link java.util.Date}, {@link java.lang.String}. The
280 * <code>value</code> can be null.
281 */
282 @Override
283 public Object getAsObject(
284 FacesContext context,
285 UIComponent component,
286 String value)
287 {
288 Date date = _getParsedDate(context, component, value);
289 if (date != null)
290 {
291 _fillTimePortion(context, component, date);
292 }
293
294 return date;
295 }
296
297 /**
298 * <p>Convert the model Date object value, into a String based on the pattern
299 * or styles.</p>
300 * @param context {@link FacesContext} for the request being processed
301 * @param component {@link UIComponent} with which this model object
302 * value is associated
303 * @param value Model object value to be converted
304 * (may be <code>null</code>)
305 *
306 * @return a zero-length String if value is <code>null</code>,
307 * otherwise the result of the conversion
308 *
309 * @exception ConverterException if conversion cannot be successfully
310 * performed
311 * @exception NullPointerException if <code>context</code> or
312 * <code>component</code> is <code>null</code>
313 */
314 @Override
315 public String getAsString(
316 FacesContext context,
317 UIComponent component,
318 Object value
319 )
320 {
321 if (context == null || component == null)
322 throw new NullPointerException(_LOG.getMessage(
323 "NULL_FACESCONTEXT_OR_UICOMPONENT"));
324
325 if (null == value)
326 return "";
327
328 if (value instanceof String)
329 return (String)value;
330
331 if (!(value instanceof Date))
332 throw new ClassCastException(_LOG.getMessage(
333 "VALUE_IS_NOT_DATE_TYPE_IT_IS", new Object[]{value,value.getClass()}));
334
335 DateFormat format = _getDateFormat(context, getPattern(), false, (Date)value);
336 return format.format(value);
337 }
338
339 /**
340 * <p>Custom error message to be used, for creating detail part of the {@link FacesMessage},
341 * for values that cannot be converted to {@link java.util.Date} when the
342 * <code>pattern / secondary pattern</code> is set or when <code>type</code>
343 * is set to <code>'date'</code>.</p>
344 * Overrides detail message identified by message id {@link #CONVERT_DATE_MESSAGE_ID}
345 * @param convertDateMessageDetail custom error message.
346 *
347 */
348 public void setMessageDetailConvertDate(String convertDateMessageDetail)
349 {
350 _facesBean.setProperty(_CONVERT_DATE_MESSAGE_DETAIL_KEY, convertDateMessageDetail);
351 }
352
353 /**
354 * <p>Return custom detail error message that was set for creating {@link FacesMessage},
355 * for values that cannot be converted to {@link java.util.Date} when
356 * <code>pattern / secondary pattern</code> is set or
357 * when <code>type</code> is set to <code>'date'</code>.</p>
358 * @return custom error message that was set.
359 * @see #setMessageDetailConvertDate(String)
360 */
361 public String getMessageDetailConvertDate()
362 {
363 Object msg = _facesBean.getProperty(_CONVERT_DATE_MESSAGE_DETAIL_KEY);
364 return ComponentUtils.resolveString(msg);
365 }
366
367 /**
368 * <p>Custom error message to be used, for creating detail part of the {@link FacesMessage},
369 * for time based value that cannot be converted to date
370 * when <code>type</code> is set to <code>'time'</code>.</p>
371 * Overrides detail message identified by message id {@link #CONVERT_TIME_MESSAGE_ID}
372 * @param convertTimeMessageDetail custom error message.
373 */
374 public void setMessageDetailConvertTime(String convertTimeMessageDetail)
375 {
376 _facesBean.setProperty(_CONVERT_TIME_MESSAGE_DETAIL_KEY, convertTimeMessageDetail);
377 }
378
379 /**
380 * <p>Return custom detail error message that was set for creating {@link FacesMessage},
381 * for values that cannot be converted to {@link java.util.Date}
382 * when <code>type</code> is set to <code>'time'</code>.
383 * @return custom error message that was set.</p>
384 * @see #setMessageDetailConvertTime(java.lang.String)
385 */
386 public String getMessageDetailConvertTime()
387 {
388 Object msg =_facesBean.getProperty(_CONVERT_TIME_MESSAGE_DETAIL_KEY);
389 return ComponentUtils.resolveString(msg);
390 }
391
392 /**
393 * <p>Custom error message to be used, for creating detail part of the {@link FacesMessage},
394 * for date-time based value that cannot be converted to {@link java.util.Date}
395 * when <code>type</code> is set to <code>'both'</code>.</p>
396 * Overrides detail message identified by message id {@link #CONVERT_BOTH_MESSAGE_ID}
397 * @param convertBothMessageDetail custom error message.
398 * @see #CONVERT_BOTH_MESSAGE_ID
399 */
400 public void setMessageDetailConvertBoth(String convertBothMessageDetail)
401 {
402 _facesBean.setProperty(_CONVERT_BOTH_MESSAGE_DETAIL_KEY, convertBothMessageDetail);
403 }
404
405 /**
406 * Return custom detail error message that was set for creating {@link FacesMessage},
407 * for values that cannot be converted to {@link java.util.Date}
408 * when <code>type</code> is set to <code>'both'</code>.
409 * @return custom error message that was set.
410 * @see #setMessageDetailConvertBoth(java.lang.String)
411 */
412 public String getMessageDetailConvertBoth()
413 {
414 Object msg = _facesBean.getProperty(_CONVERT_BOTH_MESSAGE_DETAIL_KEY);
415 return ComponentUtils.resolveString(msg);
416 }
417
418 /**
419 * <p>Custom hintDate message.</p>
420 * Overrides default hint message
421 * @param hintDate Custom hint message.
422 */
423 public void setHintDate(String hintDate)
424 {
425 _facesBean.setProperty(_HINT_DATE_KEY, hintDate);
426 }
427
428 /**
429 * <p>Return custom hintDate message.</p>
430 * @return Custom hint message.
431 * @see #setHintDate(String)
432 */
433 public String getHintDate()
434 {
435 Object obj = _facesBean.getProperty(_HINT_DATE_KEY);
436 return ComponentUtils.resolveString(obj);
437 }
438
439 /**
440 * <p>Custom hintTime message.</p>
441 * Overrides default hint message
442 * @param hintTime Custom hint message.
443 */
444 public void setHintTime(String hintTime)
445 {
446 _facesBean.setProperty(_HINT_TIME_KEY, hintTime);
447 }
448
449 /**
450 * <p>Return custom hintTime message.</p>
451 * @return Custom hint message.
452 * @see #setHintTime(String)
453 */
454 public String getHintTime()
455 {
456 Object obj = _facesBean.getProperty(_HINT_TIME_KEY);
457 return ComponentUtils.resolveString(obj);
458 }
459
460 /**
461 * <p>Custom hintBoth message.</p>
462 * Overrides default hint message
463 * @param hintBoth Custom hint message.
464 */
465 public void setHintBoth(String hintBoth)
466 {
467 _facesBean.setProperty(_HINT_BOTH_KEY, hintBoth);
468 }
469
470 /**
471 * <p>Return custom hintBoth message.</p>
472 * @return Custom hint message.
473 * @see #setHintBoth(String)
474 */
475 public String getHintBoth()
476 {
477 Object obj = _facesBean.getProperty(_HINT_BOTH_KEY);
478 return ComponentUtils.resolveString(obj);
479 }
480
481 /**
482 * Gets the existing date from the component.
483 * This date will be used to fill in missing portions of the new date.
484 * For example, if the new date is missing the time, the time portion
485 * from the existing date will be used.
486 * <P>
487 * This implementation checks to see if the component is a ValueHolder, and
488 * calls getValue() and returns the result if it is a Date instance.
489 * @param component The component to get the existing date from.
490 * @return null if there is no existing date.
491 */
492 protected Date getDate(FacesContext context, UIComponent component)
493 {
494 if (component instanceof ValueHolder)
495 {
496 Object value = ((ValueHolder)component).getValue();
497 if(value instanceof Date)
498 {
499 return (Date)value;
500 }
501 }
502 return null;
503 }
504
505 private Locale _extractConverterLocale(
506 FacesContext context)
507 {
508 Locale locale = getLocale();
509 if (null == locale)
510 {
511 RequestContext reqContext = RequestContext.getCurrentInstance();
512 if (reqContext != null)
513 {
514 locale = reqContext.getFormattingLocale();
515 }
516 if (locale == null)
517 {
518 locale = context.getViewRoot().getLocale();
519 }
520 }
521 return locale;
522 }
523
524 private Date _getParsedDate(FacesContext context,
525 UIComponent component,
526 String value)
527 {
528 if (context == null || component == null)
529 throw new NullPointerException(_LOG.getMessage(
530 "NULL_FACESCONTEXT_OR_UICOMPONENT"));
531
532 if (null == value)
533 return null;
534
535 value = value.trim();
536
537 if ( 1 > value.length() )
538 return null;
539
540 try
541 {
542 String pattern = getPattern();
543 if (pattern == null)
544 {
545
546 DateFormat format = getDateFormat(context, null, true, null);
547 if (format instanceof SimpleDateFormat)
548 {
549 pattern = ((SimpleDateFormat)format).toPattern();
550 }
551 }
552
553 if (pattern != null)
554 {
555 return _doLenientParse(context, component, value, pattern);
556 }
557 else
558 {
559
560 return _parse(context, component, value, null);
561 }
562 }
563 catch (ConverterException ce)
564 {
565 try
566 {
567
568
569 String secPattern = getSecondaryPattern();
570 if ( secPattern != null)
571 {
572 return _doLenientParse(context, component, value, secPattern);
573 }
574 }
575 catch(ConverterException secondaryCe)
576 {
577
578 ;
579 }
580 throw ce;
581 }
582 }
583
584
585 /**
586 * Fill in the time portion of the new date with the time from the previous
587 * date value if the converter displays only date. For now, we are not
588 * bothered about filling all the missing parts of the pattern. But in
589 * future we would consider that.
590 */
591 private void _fillTimePortion(
592 FacesContext context,
593 UIComponent component,
594 Date newDate)
595 {
596
597 Date prevDate = getDate(context, component);
598
599
600 if (prevDate == null)
601 {
602 return;
603 }
604
605
606 String pattern = getPattern();
607 if (pattern == null && !"date".equals(getType()))
608 {
609 return;
610 }
611
612
613 boolean fillMilliSeconds = true;
614 boolean fillSeconds = true;
615 boolean fillMinutes = true;
616 boolean fillHour = true;
617
618 if (pattern != null)
619 {
620 int patternLen = pattern.length();
621 for (int currCharIndex = 0; currCharIndex < patternLen; currCharIndex++)
622 {
623 switch (pattern.charAt(currCharIndex))
624 {
625 case 'S':
626 fillMilliSeconds = false;
627 break;
628 case 's':
629 fillSeconds = false;
630 break;
631 case 'm':
632 fillMinutes = false;
633 break;
634 case 'h':
635 case 'H':
636 case 'k':
637 case 'K':
638 fillHour = false;
639 break;
640 }
641 }
642 }
643
644
645 if ( fillMilliSeconds || fillSeconds || fillMinutes || fillHour )
646 {
647 TimeZone timeZone = _getTimeZone();
648
649
650 Calendar prevCal = Calendar.getInstance(timeZone);
651 prevCal.setTime(prevDate);
652
653 Calendar newCal = Calendar.getInstance(timeZone);
654 newCal.setTime(newDate);
655
656
657
658 if (fillMilliSeconds)
659 {
660 newCal.set(Calendar.MILLISECOND, prevCal.get(Calendar.MILLISECOND));
661 }
662
663 if (fillSeconds)
664 {
665 newCal.set(Calendar.SECOND, prevCal.get(Calendar.SECOND));
666 }
667
668 if(fillMinutes)
669 {
670 newCal.set(Calendar.MINUTE, prevCal.get(Calendar.MINUTE));
671 }
672
673 if(fillHour)
674 {
675 newCal.set(Calendar.HOUR_OF_DAY, prevCal.get(Calendar.HOUR_OF_DAY));
676 }
677
678
679 newDate.setTime(newCal.getTimeInMillis());
680 }
681 }
682
683 private Date _parse(
684 FacesContext context,
685 UIComponent component,
686 String value,
687 String pattern
688 )
689 {
690 DateFormat fmt = getDateFormat(context, pattern, true, null);
691 try
692 {
693 return fmt.parse(value);
694
695 } catch (ConverterException ce)
696 {
697 throw ce;
698 }
699 catch (ParseException pe)
700 {
701 Object[] params = _getPlaceHolderParameters(context, component, value);
702 throw new ConverterException(getParseErrorMessage(context, component,
703 pattern, params),
704 pe);
705 }
706 }
707
708 /**
709 * Does some more lenient parsing than the stric JSF standard wants.
710 */
711 private Date _doLenientParse(
712 FacesContext context,
713 UIComponent component,
714 String value,
715 String pattern
716 )
717 {
718
719
720
721
722
723 if(pattern.endsWith(" '"))
724 {
725 value += " ";
726 }
727
728
729
730
731
732
733
734
735 ConverterException ce;
736 try
737 {
738 return _parse(context, component, value, pattern);
739 }
740 catch (ConverterException convException)
741 {
742
743
744 ce = convException;
745
746 List<String> patterns = new ArrayList<String>();
747 patterns.add(pattern);
748
749 Locale locale = _extractConverterLocale(context);
750
751 if (_CONVENIENCE_PATTERNS.containsKey(locale))
752 {
753 patterns.addAll(_CONVENIENCE_PATTERNS.get(locale));
754 }
755 List<String> lenientPatterns = new ArrayList<String>();
756 for (String tmpPattern : patterns)
757 {
758 lenientPatterns.addAll(_getLenientPatterns(tmpPattern));
759 }
760
761 for (String lenientPattern : lenientPatterns)
762 {
763 try
764 {
765 return _parse(context, component, value, lenientPattern);
766 }
767 catch (ConverterException e)
768 {
769
770
771
772 continue;
773 }
774 }
775 throw ce;
776 }
777 }
778
779
780
781 /**
782 * <p>Set the <code>Locale</code> to be used when parsing or formatting
783 * dates and times. If set to <code>null</code>, the <code>Locale</code>
784 * stored in the {@link javax.faces.component.UIViewRoot} for the current
785 * request will be utilized.</p>
786 *
787 * @param locale The new <code>Locale</code> (or <code>null</code>)
788 */
789 @Override
790 public void setLocale(Locale locale)
791 {
792 _facesBean.setProperty(_LOCALE_KEY, locale);
793 }
794
795 /**
796 * <p>Return the <code>Locale</code> that was set.
797 * If not explicitly set, the <code>Locale</code> stored
798 * in the {@link javax.faces.component.UIViewRoot} for the current
799 * request is used during call to <code>getAsObject</code> and
800 * <code>getAsString</code>.</p>
801 */
802 @Override
803 public Locale getLocale()
804 {
805 Object locale = _facesBean.getProperty(_LOCALE_KEY);
806 return ComponentUtils.resolveLocale(locale);
807 }
808
809 /**
810 * <p>Set the format pattern to be used when formatting and parsing
811 * dates and times. Valid values are those supported by
812 * <code>java.text.SimpleDateFormat</code>.
813 * An invalid value will cause a {@link ConverterException} when
814 * <code>getAsObject()</code> or <code>getAsString()</code> is called.</p>
815 *
816 * @param pattern The new format pattern
817 */
818 @Override
819 public void setPattern(String pattern)
820 {
821 _facesBean.setProperty(_PATTERN_KEY, pattern);
822 }
823
824 /**
825 * <p>Return the format pattern to be used when formatting and
826 * parsing dates and times.</p>
827 */
828 @Override
829 public String getPattern()
830 {
831 Object pattern = _facesBean.getProperty(_PATTERN_KEY);
832 return ComponentUtils.resolveString(pattern);
833 }
834
835 /**
836 * <p>Set the <code>TimeZone</code> used to interpret a time value.</p>
837 *
838 * @param timeZone The new time zone
839 */
840 @Override
841 public void setTimeZone(TimeZone timeZone)
842 {
843 _facesBean.setProperty(_TIME_ZONE_KEY, timeZone);
844 }
845
846 /**
847 * <p>Return the <code>TimeZone</code> that is used to interpret a time value.
848 * If not explicitly set or if a null value is set, then during call to
849 * <code>getAsObject</code> and <code>getAsString</code>, the time zone set
850 * for the web-app is used. If time zone is not set for the web-app then
851 * the default time zone of <code>GMT</code> is used.</p>
852 */
853 @Override
854 public TimeZone getTimeZone()
855 {
856 Object timeZone = _facesBean.getProperty(_TIME_ZONE_KEY);
857 return ComponentUtils.resolveTimeZone(timeZone);
858 }
859
860 /**
861 * <p>Set the type of value to be formatted or parsed.
862 * Valid values are <code>both</code>, <code>date</code>, or
863 * <code>time</code>.
864 * An invalid value will cause a {@link IllegalStateException} when
865 * <code>getAsObject()</code> or <code>getAsString()</code> is called.</p>
866 *
867 * @param type The new date style
868 */
869 @Override
870 public void setType(String type)
871 {
872 _facesBean.setProperty(_TYPE_KEY, type);
873 }
874
875 /**
876 * <p>Return the type of value to be formatted or parsed.
877 * If not explicitly set, the default type, <code>date</code>
878 * is returned.</p>
879 */
880 @Override
881 public String getType()
882 {
883 Object type = _facesBean.getProperty(_TYPE_KEY);
884 return ComponentUtils.resolveString(type, "date");
885 }
886
887 /**
888 * <p>Set the style to be used to format or parse dates. Valid values
889 * are <code>default</code>, <code>shortish</code>
890 * <code>short</code>, <code>medium</code>,
891 * <code>long</code>, and <code>full</code>.
892 * An invalid value will cause a {@link IllegalStateException} when
893 * <code>getAsObject()</code> or <code>getAsString()</code> is called.</p>
894 *
895 * @param dateStyle The new style code
896 */
897 @Override
898 public void setDateStyle(String dateStyle)
899 {
900 _facesBean.setProperty(_DATE_STYLE_KEY, dateStyle);
901 }
902
903 /**
904 * <p>Return the style to be used to format or parse dates. If not set,
905 * the default value, <code>shortish</code>, is returned.</p>
906 * @see #setDateStyle(java.lang.String)
907 * @return date style
908 */
909 @Override
910 public String getDateStyle()
911 {
912 Object dateStyle = _facesBean.getProperty(_DATE_STYLE_KEY);
913 return ComponentUtils.resolveString(dateStyle, "shortish");
914 }
915
916 /**
917 * <p>Set the style to be used to format or parse times. Valid values
918 * are <code>default</code>, <code>short</code>,
919 * <code>medium</code>, <code>long</code>, and <code>full</code>.
920 * An invalid value will cause a {@link IllegalStateException} when
921 * <code>getAsObject()</code> or <code>getAsString()</code> is called.</p>
922 *
923 * @param timeStyle The new style code
924 */
925 @Override
926 public void setTimeStyle(String timeStyle)
927 {
928 _facesBean.setProperty(_TIME_STYLE_KEY, timeStyle);
929 }
930
931 /**
932 * <p>Return the style to be used to format or parse times. If not set,
933 * the default value, <code>short</code>, is returned.</p>
934 */
935 @Override
936 public String getTimeStyle()
937 {
938 Object timeStyle = _facesBean.getProperty(_TIME_STYLE_KEY);
939 return ComponentUtils.resolveString(timeStyle, "short");
940 }
941
942 /**
943 * <p>Second pattern which will be used to parse string in
944 * <code>getAsObject</code> if pattern or styles fail. But is never
945 * used for formatting to string in <code>getAsString()</code>.</p>
946 * @param secondaryPattern a second pattern which will be used
947 * as a second attempt to parse a string if the primary pattern or
948 * styles fail, but is never used for formatting strings.
949 */
950 public void setSecondaryPattern(String secondaryPattern)
951 {
952 _facesBean.setProperty(_SECONDARY_PATTERN_KEY, secondaryPattern);
953 }
954
955 /**
956 * <p>Return the secondary pattern used to parse string when parsing by
957 * pattern or style fails.</p>
958 */
959 public String getSecondaryPattern()
960 {
961 Object secPattern = _facesBean.getProperty(_SECONDARY_PATTERN_KEY);
962 return ComponentUtils.resolveString(secPattern);
963 }
964
965 @Override
966 public boolean isTransient()
967 {
968 return _isTransient;
969 }
970
971 @Override
972 public void setTransient(boolean isTransient)
973 {
974 _isTransient = isTransient;
975 }
976
977 @Override
978 public Object saveState(FacesContext context)
979 {
980 return _facesBean.saveState(context);
981 }
982
983 @Override
984 public void restoreState(FacesContext context, Object state)
985 {
986 _facesBean.restoreState(context, state);
987 }
988
989 /**
990 * <p>Set the {@link ValueExpression} used to calculate the value for the
991 * specified attribute if any.</p>
992 *
993 * @param name Name of the attribute for which to set a {@link ValueExpression}
994 * @param expression The {@link ValueExpression} to set, or <code>null</code>
995 * to remove any currently set {@link ValueExpression}
996 *
997 * @exception NullPointerException if <code>name</code>
998 * is <code>null</code>
999 * @exception IllegalArgumentException if <code>name</code> is not a valid
1000 * attribute of this converter
1001 */
1002 public void setValueExpression(String name, ValueExpression expression)
1003 {
1004 ConverterUtils.setValueExpression(_facesBean, name, expression) ;
1005 }
1006
1007
1008 /**
1009 * <p>Return the {@link ValueExpression} used to calculate the value for the
1010 * specified attribute name, if any.</p>
1011 *
1012 * @param name Name of the attribute or property for which to retrieve a
1013 * {@link ValueExpression}
1014 *
1015 * @exception NullPointerException if <code>name</code>
1016 * is <code>null</code>
1017 * @exception IllegalArgumentException if <code>name</code> is not a valid
1018 * attribute of this converter
1019 */
1020 public ValueExpression getValueExpression(String name)
1021 {
1022 return ConverterUtils.getValueExpression(_facesBean, name);
1023 }
1024
1025 /**
1026 * <p>Set the {@link ValueBinding} used to calculate the value for the
1027 * specified attribute if any.</p>
1028 *
1029 * @param name Name of the attribute for which to set a {@link ValueBinding}
1030 * @param binding The {@link ValueBinding} to set, or <code>null</code>
1031 * to remove any currently set {@link ValueBinding}
1032 *
1033 * @exception NullPointerException if <code>name</code>
1034 * is <code>null</code>
1035 * @exception IllegalArgumentException if <code>name</code> is not a valid
1036 * attribute of this converter
1037 * @deprecated
1038 */
1039 public void setValueBinding(String name, ValueBinding binding)
1040 {
1041 ConverterUtils.setValueBinding(_facesBean, name, binding) ;
1042 }
1043
1044
1045 /**
1046 * <p>Return the {@link ValueBinding} used to calculate the value for the
1047 * specified attribute name, if any.</p>
1048 *
1049 * @param name Name of the attribute or property for which to retrieve a
1050 * {@link ValueBinding}
1051 *
1052 * @exception NullPointerException if <code>name</code>
1053 * is <code>null</code>
1054 * @exception IllegalArgumentException if <code>name</code> is not a valid
1055 * attribute of this converter
1056 * @deprecated
1057 */
1058 public ValueBinding getValueBinding(String name)
1059 {
1060 return ConverterUtils.getValueBinding(_facesBean, name);
1061 }
1062
1063 /**
1064 * <p>Compares this DateTimeConverter with the specified Object for
1065 * equality.</p>
1066 * @param object Object to which this DateTimeConverter is to be compared.
1067 * @return true if and only if the specified Object is a DateTimeConverter
1068 * and if all parameters are equal.
1069 */
1070 @Override
1071 public boolean equals(Object object)
1072 {
1073 if (this == object)
1074 return true;
1075
1076 if(object instanceof DateTimeConverter)
1077 {
1078 DateTimeConverter other = (DateTimeConverter)object;
1079
1080 if ( (isTransient() == other.isTransient())
1081 && ConverterUtils.equals(getDateStyle(), other.getDateStyle())
1082 && ConverterUtils.equals(getLocale(), other.getLocale())
1083 && ConverterUtils.equals(getPattern(), other.getPattern())
1084 && ConverterUtils.equals(getTimeStyle(), other.getTimeStyle())
1085 && ConverterUtils.equals(getTimeZone(), other.getTimeZone())
1086 && ConverterUtils.equals(getType(), other.getType())
1087 && ConverterUtils.equals(getSecondaryPattern(), other.getSecondaryPattern())
1088 && ConverterUtils.equals(getMessageDetailConvertDate(),
1089 other.getMessageDetailConvertDate())
1090 && ConverterUtils.equals(getMessageDetailConvertTime(),
1091 other.getMessageDetailConvertTime())
1092 && ConverterUtils.equals(getMessageDetailConvertBoth(),
1093 other.getMessageDetailConvertBoth())
1094 )
1095 {
1096 return true;
1097 }
1098 }
1099 return false;
1100 }
1101
1102 /**
1103 * <p>Returns the hash code for this Converter.</p>
1104 * @return a hash code value for this object.
1105 */
1106 @Override
1107 public int hashCode()
1108 {
1109 int result = 17;
1110 result = result * 37 + (isTransient()? 1 : 0);
1111 result = result * 37 + _getHashValue(getDateStyle());
1112 result = result * 37 + _getHashValue(getLocale());
1113 result = result * 37 + _getHashValue(getPattern());
1114 result = result * 37 + _getHashValue(getTimeStyle());
1115 result = result * 37 + _getHashValue(getTimeZone());
1116 result = result * 37 + _getHashValue(getType());
1117 result = result * 37 + _getHashValue(getSecondaryPattern());
1118 result = result * 37 + _getHashValue(getMessageDetailConvertDate());
1119 result = result * 37 + _getHashValue(getMessageDetailConvertTime());
1120 result = result * 37 + _getHashValue(getMessageDetailConvertBoth());
1121 return result;
1122 }
1123
1124 protected final DateFormat getDateFormat(
1125 FacesContext context,
1126 String pattern,
1127 boolean forParsing,
1128 Date targetDate
1129 ) throws ConverterException
1130 {
1131 ConverterException exception = null;
1132 try
1133 {
1134 DateFormat format = _getDateFormat(context, pattern, forParsing, targetDate);
1135 return format;
1136 }
1137 catch (ConverterException ce)
1138 {
1139 exception = ce;
1140 }
1141 catch (Exception e)
1142 {
1143 exception = new ConverterException(e);
1144 }
1145 throw exception;
1146 }
1147
1148 /**
1149 * Returns the TimeZone that will be set on DateFormat for formatting
1150 * and parsing the dates. By default, this just returns the specified
1151 * time zone, the one that is set on the DateTimeConverter or in the
1152 * Adf-Faces config.
1153 */
1154 protected TimeZone getFormattingTimeZone(TimeZone tZone)
1155 {
1156 return getFormattingTimeZone (tZone, null);
1157 }
1158
1159 /**
1160 * Returns the timeZone for formatting and parsing the date.
1161 * TRINIDAD-1512: In some cases,timezone varies depending on the targetDate,
1162 * e.g. daylight savings.
1163 */
1164 protected TimeZone getFormattingTimeZone(TimeZone tZone, Date targetDate)
1165 {
1166 return tZone;
1167 }
1168
1169
1170
1171 private String[] _getExpectedPatterns(FacesContext context)
1172 {
1173 String pattern = getPattern();
1174
1175 if ( pattern != null )
1176 {
1177 return _getAllowedPatterns(context, pattern, getSecondaryPattern());
1178 }
1179 else
1180 {
1181 String datePattern = null;
1182
1183 try
1184 {
1185 DateFormat format = getDateFormat(context, null,false, null);
1186 if ((format != null) && (format instanceof SimpleDateFormat))
1187 {
1188 datePattern = ((SimpleDateFormat)format).toPattern();
1189 }
1190 }
1191 catch (ConverterException ce)
1192 {
1193
1194 ;
1195 }
1196 return _getAllowedPatterns(context, datePattern, getSecondaryPattern());
1197 }
1198 }
1199
1200 protected final FacesMessage getParseErrorMessage(
1201 FacesContext context,
1202 UIComponent component,
1203 String pattern,
1204 Object[] params
1205 )
1206 {
1207
1208
1209 String key = getViolationMessageKey(pattern);
1210 return _getConvertErrorFacesMessage(context, key, params, component);
1211
1212 }
1213
1214 protected final String getExample(FacesContext context)
1215 {
1216 String[] expectedPatterns = _getExpectedPatterns(context);
1217
1218 assert((expectedPatterns != null) && (expectedPatterns.length >= 1));
1219 String example = expectedPatterns[0];
1220 return example;
1221 }
1222
1223 private String[] _getAllowedPatterns(
1224 FacesContext context,
1225 String mainPattern,
1226 String secondaryPattern
1227 )
1228 {
1229 String[] patterns;
1230
1231 if (mainPattern != null)
1232 {
1233 if (secondaryPattern != null)
1234 {
1235 patterns = new String[]{mainPattern, secondaryPattern};
1236 }
1237 else
1238 {
1239 patterns = new String[]{mainPattern};
1240 }
1241 }
1242 else
1243 {
1244 patterns = new String[]{secondaryPattern};
1245 }
1246
1247
1248 for (int i = 0; i < patterns.length; i++)
1249 {
1250 patterns[i] = _getExample(context, patterns[i]);
1251 }
1252
1253 return patterns;
1254 }
1255
1256 /**
1257 * <p>Return the style constant for the specified style name.</p>
1258 * If invalid throw IllegalStateException.
1259 *
1260 * @param dateStyle Name of the date style for which to return a constant
1261 *
1262 */
1263 private static final int _getDateStyle(String dateStyle)
1264 {
1265 if (dateStyle.equals("shortish"))
1266 {
1267 return _SHORTISH;
1268 }
1269 else if (dateStyle.equals("default"))
1270 {
1271 return (DateFormat.DEFAULT);
1272 }
1273 else if (dateStyle.equals("short"))
1274 {
1275 return (DateFormat.SHORT);
1276 }
1277 else if (dateStyle.equals("medium"))
1278 {
1279 return (DateFormat.MEDIUM);
1280 }
1281 else if (dateStyle.equals("long"))
1282 {
1283 return (DateFormat.LONG);
1284 }
1285 else if (dateStyle.equals("full"))
1286 {
1287 return (DateFormat.FULL);
1288 }
1289 else
1290 throw new IllegalStateException(_LOG.getMessage(
1291 "INVALID_DATE_STYLE", dateStyle));
1292 }
1293
1294 private static final int _getTimeStyle(String timeStyle)
1295 {
1296 if ("default".equals(timeStyle))
1297 {
1298 return (DateFormat.DEFAULT);
1299 }
1300 else if ("short".equals(timeStyle))
1301 {
1302 return (DateFormat.SHORT);
1303 }
1304 else if ("medium".equals(timeStyle))
1305 {
1306 return (DateFormat.MEDIUM);
1307 }
1308 else if ("long".equals(timeStyle))
1309 {
1310 return (DateFormat.LONG);
1311 }
1312 else if ("full".equals(timeStyle))
1313 {
1314 return (DateFormat.FULL);
1315 }
1316 else
1317 throw new IllegalStateException(_LOG.getMessage(
1318 "INVALID_TIME_STYLE", timeStyle));
1319 }
1320
1321 /**
1322 * <p>The valid values for type are date,time and both. Any value other than this
1323 * would result in a ConverterException.</p>
1324 * @return type
1325 */
1326 private static int _getType(String type)
1327 {
1328 if ("date".equals(type))
1329 return _TYPE_DATE;
1330 else if ("time".equals(type))
1331 return _TYPE_TIME;
1332 else if ("both".equals(type))
1333 return _TYPE_BOTH;
1334 else
1335 throw new IllegalStateException(_LOG.getMessage(
1336 "INVALID_TYPE", type));
1337 }
1338
1339
1340
1341 private static int _getHashValue(Object obj)
1342 {
1343 return obj == null? 0 : obj.hashCode();
1344 }
1345
1346 private static List<String> _getLenientPatterns(String pattern)
1347 {
1348
1349
1350
1351
1352
1353
1354
1355
1356 List<String> patterns = new ArrayList<String>();
1357
1358 patterns.add(pattern);
1359
1360 String[] leniencyApplicablePatterns = new String[1];
1361 leniencyApplicablePatterns[0] = pattern;
1362
1363 if (pattern.indexOf("MMM") != -1)
1364 {
1365 leniencyApplicablePatterns = new String[3];
1366 leniencyApplicablePatterns[0] = pattern;
1367
1368 String str1 = pattern.replaceAll("MMM", "MM");
1369 patterns.add(str1);
1370 leniencyApplicablePatterns[1] = str1;
1371
1372 String str2 = pattern.replaceAll("MMM", "M");
1373 leniencyApplicablePatterns[2] = str2;
1374 patterns.add(str2);
1375 }
1376
1377
1378
1379 int len = leniencyApplicablePatterns.length;
1380 if (pattern.indexOf('/') != -1)
1381 {
1382 for (int i = 0; i < len; i++)
1383 patterns.add(leniencyApplicablePatterns[i].replaceAll("/", "-"));
1384
1385 for (int i = 0; i < len; i++)
1386 patterns.add(leniencyApplicablePatterns[i].replaceAll("/", "."));
1387 }
1388 else if (pattern.indexOf('-') != -1)
1389 {
1390 for (int i = 0; i < len; i++)
1391 patterns.add(leniencyApplicablePatterns[i].replaceAll("-", "/"));
1392
1393 for (int i = 0; i < len; i++)
1394 patterns.add(leniencyApplicablePatterns[i].replaceAll("-", "."));
1395 }
1396 else if (pattern.indexOf('.') != -1)
1397 {
1398 for (int i = 0; i < len; i++)
1399 patterns.add(leniencyApplicablePatterns[i].replaceAll("\\.", "/"));
1400
1401 for (int i = 0; i < len; i++)
1402 patterns.add(leniencyApplicablePatterns[i].replaceAll("\\.", "-"));
1403 }
1404
1405 return patterns;
1406 }
1407
1408 private Object[] _getPlaceHolderParameters(
1409 FacesContext context,
1410 UIComponent component,
1411 String value)
1412 {
1413 Object label = ConverterUtils.getComponentLabel(component);
1414 String example = getExample(context);
1415 Object[] params = {label, value, example};
1416 return params;
1417 }
1418
1419 private Object _getRawConvertBothMessageDetail()
1420 {
1421 return _facesBean.getRawProperty(_CONVERT_BOTH_MESSAGE_DETAIL_KEY);
1422 }
1423
1424 private Object _getRawConvertDateMessageDetail()
1425 {
1426 return _facesBean.getRawProperty(_CONVERT_DATE_MESSAGE_DETAIL_KEY);
1427 }
1428
1429 private Object _getRawConvertTimeMessageDetail()
1430 {
1431 return _facesBean.getRawProperty(_CONVERT_TIME_MESSAGE_DETAIL_KEY);
1432 }
1433
1434 private FacesMessage _getConvertErrorFacesMessage(
1435 FacesContext context,
1436 String key,
1437 Object[] params,
1438 UIComponent component
1439 )
1440 {
1441 Object msgPattern = getMessagePattern(context, key, params, component);
1442 return MessageFactory.getMessage(context, key, msgPattern,
1443 params, component);
1444 }
1445
1446 private String _getExample(FacesContext context, String pattern)
1447 {
1448 DateFormat format = _getDateFormat(context, pattern, false, _EXAMPLE_DATE);
1449 return format.format(_EXAMPLE_DATE);
1450 }
1451
1452
1453 protected Object getMessagePattern(
1454 FacesContext context,
1455 String key,
1456 Object[] params,
1457 UIComponent component
1458 )
1459 {
1460 Object msgPattern;
1461 if (CONVERT_DATE_MESSAGE_ID.equals(key))
1462 {
1463 msgPattern = _getRawConvertDateMessageDetail();
1464 }
1465 else if (CONVERT_TIME_MESSAGE_ID.equals(key))
1466 {
1467 msgPattern = _getRawConvertTimeMessageDetail();
1468 }
1469 else if (CONVERT_BOTH_MESSAGE_ID.equals(key))
1470 {
1471 msgPattern = _getRawConvertBothMessageDetail();
1472 }
1473 else
1474 {
1475
1476 throw new IllegalArgumentException(_LOG.getMessage(
1477 "ILLEGAL_MESSAGE_ID", key));
1478 }
1479
1480 return msgPattern;
1481 }
1482
1483 protected String getViolationMessageKey(String pattern)
1484 {
1485 String key = null;
1486
1487 if (getPattern() == null || pattern == null)
1488 {
1489 String type = getType();
1490 if("date".equals(type))
1491 {
1492 key = CONVERT_DATE_MESSAGE_ID;
1493 }
1494 else if ("time".equals(type))
1495 {
1496 key = CONVERT_TIME_MESSAGE_ID;
1497 }
1498 else if ("both".equals(type))
1499 {
1500 key = CONVERT_BOTH_MESSAGE_ID;
1501 }
1502 else if (pattern == null)
1503 {
1504
1505
1506 throw new IllegalArgumentException(_LOG.getMessage(
1507 "ILLEGAL_ATTRIBUTE_TYPE_VALUE", type));
1508 }
1509 }
1510
1511 if (key == null)
1512 {
1513
1514 key = CONVERT_DATE_MESSAGE_ID;
1515 }
1516
1517 return key;
1518 }
1519
1520 private SimpleDateFormat _getSimpleDateFormat(String pattern, Locale locale)
1521 {
1522 String variant = locale.getVariant();
1523 SimpleDateFormat sdf = null;
1524
1525 if ((variant != null) && (variant.toUpperCase().startsWith("ORACLE")))
1526 {
1527
1528
1529 try
1530 {
1531 ResourceBundle oraElementsData =
1532 ResourceBundle.getBundle(_ORA_LOCALE_ELEMENTS_BASE, locale);
1533
1534 DateFormatSymbols syms = new DateFormatSymbols(locale);
1535
1536 String[] res = oraElementsData.getStringArray("AmPmMarkers");
1537 if (res != null)
1538 syms.setAmPmStrings(res);
1539 res = oraElementsData.getStringArray("Eras");
1540 if (res != null)
1541 syms.setEras(res);
1542 res = oraElementsData.getStringArray("MonthNames");
1543 if (res != null)
1544 syms.setMonths(res);
1545 res = oraElementsData.getStringArray("MonthAbbreviations");
1546 if (res != null)
1547 syms.setShortMonths(res);
1548 res = oraElementsData.getStringArray("DayAbbreviations");
1549 if (res != null)
1550 syms.setShortWeekdays(res);
1551 res = oraElementsData.getStringArray("DayNames");
1552 if (res != null)
1553 syms.setWeekdays(res);
1554 sdf = new SimpleDateFormat(pattern, syms);
1555 }
1556 catch (MissingResourceException e)
1557 {
1558
1559 sdf = new SimpleDateFormat(pattern, locale);
1560 }
1561 }
1562 else
1563 sdf = new SimpleDateFormat(pattern, locale);
1564
1565 return sdf;
1566 }
1567
1568 /**
1569 * Returns a SimpleDateFormat based on the passed in SimpleDateFormat that
1570 * uses at least 4 digit years if it uses years at all.
1571 * <p>
1572 * If <code>format</code> already uses at least 4 digit years, then
1573 * the <code>format</code> instance will be returned from this function
1574 * unchanged.
1575 */
1576 private SimpleDateFormat _get4YearFormat(
1577 SimpleDateFormat format,
1578 Locale loc
1579 )
1580 {
1581 String formatPattern = format.toPattern();
1582
1583
1584
1585
1586 int patternLen = formatPattern.length();
1587
1588 int firstYIndex = -1;
1589 int yCount = 0;
1590 boolean inQuotes = false;
1591
1592 int currCharIndex = 0;
1593
1594 for (; currCharIndex < patternLen; currCharIndex++)
1595 {
1596 char currChar = formatPattern.charAt(currCharIndex);
1597
1598 switch (currChar)
1599 {
1600 case 'y':
1601 if (!inQuotes)
1602 {
1603
1604 if (firstYIndex < 0)
1605 firstYIndex = currCharIndex;
1606
1607 yCount++;
1608 }
1609 break;
1610
1611 case '\'':
1612 if (inQuotes)
1613 {
1614 int nextCharIndex = currCharIndex + 1;
1615
1616 if ((nextCharIndex < patternLen) &&
1617 ('\'' == formatPattern.charAt(nextCharIndex)))
1618 {
1619
1620 currCharIndex++;
1621 }
1622 else
1623 {
1624
1625 inQuotes = false;
1626 }
1627 }
1628 else
1629 {
1630
1631 inQuotes = true;
1632 }
1633
1634
1635 default:
1636 {
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646 }
1647 }
1648 }
1649
1650
1651
1652
1653
1654
1655 if ((yCount > 0) && (yCount < 4))
1656 {
1657 StringBuffer newFormatPattern = new StringBuffer(patternLen + 4 - yCount);
1658
1659
1660 if (firstYIndex > 0)
1661 {
1662 newFormatPattern.append(formatPattern.substring(0, firstYIndex));
1663 }
1664
1665
1666 for (; yCount < 4; yCount++)
1667 {
1668 newFormatPattern.append('y');
1669 }
1670
1671
1672 if (firstYIndex < patternLen)
1673 {
1674 newFormatPattern.append(formatPattern.substring(firstYIndex,
1675 patternLen));
1676 }
1677
1678
1679
1680 format = _getSimpleDateFormat(newFormatPattern.toString(), loc);
1681 }
1682
1683 return format;
1684 }
1685
1686 private DateFormat _getDateFormat(
1687 FacesContext context,
1688 String pattern,
1689 boolean forParsing,
1690 Date targetDate
1691 )
1692 {
1693 Locale locale = _extractConverterLocale(context);
1694 TimeZone tZone = _getTimeZone();
1695
1696 DateFormat format = _getCachedFormat(locale, tZone, pattern);
1697
1698 if (format != null)
1699 {
1700 format.setTimeZone(tZone);
1701 }
1702 else
1703 {
1704
1705
1706
1707
1708 if ( null == pattern || "".equals(pattern))
1709 {
1710 int type = _getType(getType());
1711
1712 if (type == _TYPE_DATE || type == _TYPE_BOTH)
1713 {
1714 int actualDateStyle = _getDateStyle(getDateStyle());
1715 int dateStyle = _changeShortishAsShortIfNeeded(actualDateStyle);
1716
1717 if (type == _TYPE_DATE)
1718 {
1719 format = DateFormat.getDateInstance(dateStyle, locale);
1720 }
1721 else
1722 {
1723 int timeStyle = _getTimeStyle(getTimeStyle());
1724 format = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
1725 }
1726 }
1727 if (type == _TYPE_TIME)
1728 {
1729 int timeStyle = _getTimeStyle(getTimeStyle());
1730 format = DateFormat.getTimeInstance(timeStyle, locale);
1731 }
1732 }
1733 else
1734 {
1735
1736 format = _getSimpleDateFormat(pattern, locale);
1737 }
1738
1739 if (format instanceof SimpleDateFormat && !forParsing)
1740 {
1741 SimpleDateFormat simpleFormat = (SimpleDateFormat)format;
1742
1743
1744
1745
1746
1747
1748
1749 if (null == pattern && "shortish".equals(getDateStyle()) )
1750 {
1751 int type = _getType(getType());
1752 if (type == _TYPE_DATE || type == _TYPE_BOTH )
1753 {
1754 simpleFormat = _get4YearFormat(simpleFormat, locale);
1755 format = simpleFormat;
1756 }
1757 }
1758
1759 Calendar cal;
1760 RequestContext reqContext = RequestContext.getCurrentInstance();
1761 if (reqContext == null)
1762 {
1763 cal = null;
1764 if(_LOG.isWarning())
1765 {
1766 _LOG.warning("NO_REQUESTCONTEXT_TWO_DIGIT_YEAR_START_DEFAULT");
1767 }
1768 }
1769 else
1770 {
1771 cal = new GregorianCalendar(reqContext.getTwoDigitYearStart(), 0, 0);
1772 }
1773 if (cal != null)
1774 simpleFormat.set2DigitYearStart(cal.getTime());
1775 }
1776
1777
1778 format.setLenient(false);
1779
1780
1781
1782 if (tZone != null)
1783 {
1784 TimeZone formatTZone = getFormattingTimeZone(tZone, targetDate);
1785 format.setTimeZone(formatTZone);
1786 }
1787
1788
1789 _cacheFormat(format, locale, tZone, pattern);
1790 }
1791
1792 return format;
1793 }
1794
1795 private int _changeShortishAsShortIfNeeded(int dateStyle)
1796 {
1797 if (dateStyle == _SHORTISH)
1798 dateStyle = DateFormat.SHORT;
1799 return dateStyle;
1800 }
1801
1802
1803
1804
1805
1806 private DateFormat _getCachedFormat(
1807 Locale locale,
1808 TimeZone tZone,
1809 String pattern
1810 )
1811 {
1812
1813 return null;
1814 }
1815
1816
1817 private void _cacheFormat(
1818 DateFormat format,
1819 Locale locale,
1820 TimeZone tZone,
1821 String pattern
1822 )
1823 {
1824
1825
1826
1827
1828 }
1829
1830 private TimeZone _getTimeZone()
1831 {
1832 TimeZone tZone = getTimeZone();
1833
1834 if (tZone == null)
1835 {
1836 RequestContext context = RequestContext.getCurrentInstance();
1837 if (context == null)
1838 {
1839 _LOG.warning("NO_REQUESTCONTEXT_TIMEZONE_DEFAULT");
1840 }
1841 else
1842 {
1843 tZone = context.getTimeZone();
1844 }
1845
1846
1847
1848 if (tZone == null)
1849 {
1850 tZone = _DEFAULT_TIME_ZONE;
1851 }
1852 }
1853
1854 return tZone;
1855 }
1856
1857 private static final FacesBean.Type _TYPE = new FacesBean.Type();
1858
1859 private static final PropertyKey _DATE_STYLE_KEY
1860 = _TYPE.registerKey("dateStyle", String.class, "shortish");
1861
1862 private static final PropertyKey _LOCALE_KEY
1863 = _TYPE.registerKey("locale", Locale.class);
1864
1865 private static final PropertyKey _PATTERN_KEY
1866 = _TYPE.registerKey("pattern", String.class);
1867
1868 private static final PropertyKey _SECONDARY_PATTERN_KEY
1869 = _TYPE.registerKey("secondaryPattern", String.class);
1870
1871 private static final PropertyKey _TIME_STYLE_KEY
1872 = _TYPE.registerKey("timeStyle", String.class, "short");
1873
1874 private static final PropertyKey _TIME_ZONE_KEY
1875 = _TYPE.registerKey("timeZone", TimeZone.class);
1876
1877 private static final PropertyKey _TYPE_KEY
1878 = _TYPE.registerKey("type", String.class, "date");
1879
1880 private static final PropertyKey _CONVERT_DATE_MESSAGE_DETAIL_KEY
1881 = _TYPE.registerKey("messageDetailConvertDate", String.class);
1882
1883 private static final PropertyKey _CONVERT_TIME_MESSAGE_DETAIL_KEY
1884 = _TYPE.registerKey("messageDetailConvertTime", String.class);
1885
1886 private static final PropertyKey _CONVERT_BOTH_MESSAGE_DETAIL_KEY
1887 = _TYPE.registerKey("messageDetailConvertBoth", String.class);
1888
1889 private static final PropertyKey _HINT_DATE_KEY =
1890 _TYPE.registerKey("hintDate", String.class);
1891
1892 private static final PropertyKey _HINT_TIME_KEY =
1893 _TYPE.registerKey("hintTime", String.class);
1894
1895 private static final PropertyKey _HINT_BOTH_KEY =
1896 _TYPE.registerKey("hintBoth", String.class);
1897
1898 private FacesBean _facesBean = ConverterUtils.getFacesBean(_TYPE);
1899
1900 private boolean _isTransient;
1901
1902 private static final TimeZone _DEFAULT_TIME_ZONE = TimeZone.getTimeZone("GMT");
1903
1904 private static final int _SHORTISH = -2;
1905
1906 private static final int _TYPE_DATE = 0;
1907
1908 private static final int _TYPE_TIME = 2;
1909
1910 private static final int _TYPE_BOTH = 4;
1911
1912 private static final String _ORA_LOCALE_ELEMENTS_BASE =
1913 "org.apache.myfaces.trinidad.resource.LocaleElements";
1914
1915 private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(DateTimeConverter.class);
1916 private static final Date _EXAMPLE_DATE;
1917 /**
1918 * All entries added to this map MUST also be added to the client map:
1919 * trinidad-impl\src\main\javascript\META-INF\adf\jsLibs\DateFormat.js->_CONVENIENCE_PATTERNS
1920 * (in TrDateTimeConverter.prototype._initConveniencePatterns)
1921 */
1922 private static final Map<Locale, List<String>> _CONVENIENCE_PATTERNS =
1923 new HashMap<Locale, List<String>>();
1924 private static final List<String> _US_CONVENIENCE_PATTERNS =
1925 Arrays.asList("MMMM dd, yy", "MMMM/dd/yy", "dd-MMMM-yy");
1926
1927 static
1928 {
1929 Calendar dateFactory = Calendar.getInstance();
1930 dateFactory.set(1998, 10, 29, 15, 45);
1931 _EXAMPLE_DATE = dateFactory.getTime();
1932
1933
1934
1935
1936 _CONVENIENCE_PATTERNS.put(Locale.US, _US_CONVENIENCE_PATTERNS);
1937 }
1938 }