View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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.util.Date;
25  import java.util.Locale;
26  import java.util.TimeZone;
27  
28  import javax.faces.component.PartialStateHolder;
29  import javax.faces.component.UIComponent;
30  import javax.faces.context.FacesContext;
31  
32  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
33  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
34  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
35  
36  /**
37   * This tag associates a date time converter with the nearest parent UIComponent.
38   * 
39   * Unless otherwise specified, all attributes accept static values or EL expressions.
40   * 
41   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
42   */
43  @JSFConverter(
44      name="f:convertDateTime",
45      bodyContent="empty",
46      tagClass="org.apache.myfaces.taglib.core.ConvertDateTimeTag")
47  @JSFJspProperty(
48      name="binding", 
49      returnType = "javax.faces.convert.DateTimeConverter",
50      longDesc = "A ValueExpression that evaluates to a DateTimeConverter.")
51  public class DateTimeConverter
52          implements Converter, PartialStateHolder
53  {
54  
55      // API field
56      public static final String CONVERTER_ID = "javax.faces.DateTime";
57      public static final String DATE_ID = "javax.faces.converter.DateTimeConverter.DATE";
58      public static final String DATETIME_ID = "javax.faces.converter.DateTimeConverter.DATETIME";
59      public static final String STRING_ID = "javax.faces.converter.STRING";
60      public static final String TIME_ID = "javax.faces.converter.DateTimeConverter.TIME";
61  
62      // internal constants
63      private static final String TYPE_DATE = "date";
64      private static final String TYPE_TIME = "time";
65      private static final String TYPE_BOTH = "both";
66      private static final String STYLE_DEFAULT = "default";
67      private static final String STYLE_MEDIUM = "medium";
68      private static final String STYLE_SHORT = "short";
69      private static final String STYLE_LONG = "long";
70      private static final String STYLE_FULL = "full";
71      private static final TimeZone TIMEZONE_DEFAULT = TimeZone.getTimeZone("GMT");
72  
73      private String _dateStyle;
74      private Locale _locale;
75      private String _pattern;
76      private String _timeStyle;
77      private TimeZone _timeZone;
78      private String _type;
79      private boolean _transient;
80  
81      // CONSTRUCTORS
82      public DateTimeConverter()
83      {
84      }
85  
86      // METHODS
87      public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
88      {
89          if (facesContext == null)
90          {
91              throw new NullPointerException("facesContext");
92          }
93          if (uiComponent == null)
94          {
95              throw new NullPointerException("uiComponent");
96          }
97  
98          if (value != null)
99          {
100             value = value.trim();
101             if (value.length() > 0)
102             {
103                 DateFormat format = getDateFormat();
104                 TimeZone tz = getTimeZone();
105                 if( tz != null )
106                 {
107                     format.setTimeZone(tz);
108                 }
109                 try
110                 {
111                     return format.parse(value);
112                 }
113                 catch (ParseException e)
114                 {
115                     String type = getType();
116                     Object[] args = new Object[]{value,
117                             format.format(new Date()),_MessageUtils.getLabel(facesContext, uiComponent)};
118                     
119                     if(type.equals(TYPE_DATE))
120                     {
121                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, DATE_ID, args));
122                     }
123                     else if (type.equals(TYPE_TIME))
124                     {
125                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, TIME_ID, args));
126                     }
127                     else if (type.equals(TYPE_BOTH))
128                     {
129                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, DATETIME_ID, args));
130                     }
131                     else
132                     {
133                         throw new ConverterException("invalid type '" + _type + "'");
134                     }
135                 }
136             }
137         }
138         return null;
139     }
140 
141     public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
142     {
143         if (facesContext == null)
144         {
145             throw new NullPointerException("facesContext");
146         }
147         if (uiComponent == null)
148         {
149             throw new NullPointerException("uiComponent");
150         }
151 
152         if (value == null)
153         {
154             return "";
155         }
156         if (value instanceof String)
157         {
158             return (String)value;
159         }
160 
161         DateFormat format = getDateFormat();
162         TimeZone tz = getTimeZone(); 
163         if (tz != null)
164         {
165             format.setTimeZone(tz);
166         }
167         try
168         {
169             return format.format(value);
170         }
171         catch (Exception e)
172         {
173             throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID,
174                     new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
175         }
176     }
177 
178     private DateFormat getDateFormat()
179     {
180         String type = getType();
181         DateFormat format;
182         if (_pattern != null)
183         {
184             try 
185             {
186                 format = new SimpleDateFormat(_pattern, getLocale());
187             } 
188                 catch (IllegalArgumentException iae)
189             {
190                 throw new ConverterException("Invalid pattern", iae);    
191             }
192         }
193         else if (type.equals(TYPE_DATE))
194         {
195             format = DateFormat.getDateInstance(calcStyle(getDateStyle()), getLocale());
196         }
197         else if (type.equals(TYPE_TIME))
198         {
199             format = DateFormat.getTimeInstance(calcStyle(getTimeStyle()), getLocale());
200         }
201         else if (type.equals(TYPE_BOTH))
202         {
203             format = DateFormat.getDateTimeInstance(calcStyle(getDateStyle()),
204                                                     calcStyle(getTimeStyle()),
205                                                     getLocale());
206         }
207         else
208         {
209             throw new ConverterException("invalid type '" + _type + "'");
210         }
211         
212         // format cannot be lenient (JSR-127)
213         format.setLenient(false);
214         return format;
215     }
216 
217     private int calcStyle(String name)
218     {
219         if (name.equals(STYLE_DEFAULT))
220         {
221             return DateFormat.DEFAULT;
222         }
223         if (name.equals(STYLE_MEDIUM))
224         {
225             return DateFormat.MEDIUM;
226         }
227         if (name.equals(STYLE_SHORT))
228         {
229             return DateFormat.SHORT;
230         }
231         if (name.equals(STYLE_LONG))
232         {
233             return DateFormat.LONG;
234         }
235         if (name.equals(STYLE_FULL))
236         {
237             return DateFormat.FULL;
238         }
239 
240         throw new ConverterException("invalid style '" + name + "'");
241     }
242 
243     // STATE SAVE/RESTORE
244     public void restoreState(FacesContext facesContext, Object state)
245     {
246         if (state != null)
247         {
248             Object[] values = (Object[])state;
249             _dateStyle = (String)values[0];
250             _locale = (Locale)values[1];
251             _pattern = (String)values[2];
252             _timeStyle = (String)values[3];
253             _timeZone = (TimeZone)values[4];
254             _type = (String)values[5];
255         }
256     }
257 
258     public Object saveState(FacesContext facesContext)
259     {
260         if (!initialStateMarked())
261         {
262             Object[] values = new Object[6];
263             values[0] = _dateStyle;
264             values[1] = _locale;
265             values[2] = _pattern;
266             values[3] = _timeStyle;
267             values[4] = _timeZone;
268             values[5] = _type;
269             return values;
270         }
271         return null;
272     }
273 
274     // GETTER & SETTER
275     
276     /**
277      * The style of the date.  Values include: default, short, medium, 
278      * long, and full.
279      * 
280      */
281     @JSFProperty
282     public String getDateStyle()
283     {
284         return _dateStyle != null ? _dateStyle : STYLE_DEFAULT;
285     }
286 
287     public void setDateStyle(String dateStyle)
288     {
289         //TODO: validate timeStyle
290         _dateStyle = dateStyle;
291         clearInitialState();
292     }
293 
294     /**
295      * The name of the locale to be used, instead of the default.
296      * 
297      */
298     @JSFProperty
299     public Locale getLocale()
300     {
301         if (_locale != null)
302         {
303             return _locale;
304         }
305         FacesContext context = FacesContext.getCurrentInstance();
306         return context.getViewRoot().getLocale();
307     }
308 
309     public void setLocale(Locale locale)
310     {
311         _locale = locale;
312         clearInitialState();
313     }
314 
315     /**
316      * A custom Date formatting pattern, in the format used by java.text.SimpleDateFormat.
317      * 
318      */
319     @JSFProperty
320     public String getPattern()
321     {
322         return _pattern;
323     }
324 
325     public void setPattern(String pattern)
326     {
327         _pattern = pattern;
328         clearInitialState();
329     }
330 
331     /**
332      * The style of the time.  Values include:  default, short, medium, long, 
333      * and full.
334      * 
335      */
336     @JSFProperty
337     public String getTimeStyle()
338     {
339         return _timeStyle != null ? _timeStyle : STYLE_DEFAULT;
340     }
341 
342     public void setTimeStyle(String timeStyle)
343     {
344         //TODO: validate timeStyle
345         _timeStyle = timeStyle;
346         clearInitialState();
347     }
348 
349     /**
350      * The time zone to use instead of GMT (the default timezone). When
351      * this value is a value-binding to a TimeZone instance, that
352      * timezone is used. Otherwise this value is treated as a String
353      * containing a timezone id, ie as the ID parameter of method
354      * java.util.TimeZone.getTimeZone(String).
355      * 
356      */
357     @JSFProperty
358     public TimeZone getTimeZone()
359     {
360         return _timeZone != null ? _timeZone : TIMEZONE_DEFAULT;
361     }
362 
363     public void setTimeZone(TimeZone timeZone)
364     {
365         _timeZone = timeZone;
366         clearInitialState();
367     }
368 
369     public boolean isTransient()
370     {
371         return _transient;
372     }
373 
374     public void setTransient(boolean aTransient)
375     {
376         _transient = aTransient;
377     }
378 
379     /**
380      * Specifies whether the date, time, or both should be 
381      * parsed/formatted.  Values include:  date, time, and both.
382      * Default based on setting of timeStyle and dateStyle.
383      * 
384      */
385     @JSFProperty
386     public String getType()
387     {
388         return _type != null ? _type : TYPE_DATE;
389     }
390 
391     public void setType(String type)
392     {
393         //TODO: validate type
394         _type = type;
395         clearInitialState();
396     }
397     
398     private boolean _initialStateMarked = false;
399 
400     public void clearInitialState()
401     {
402         _initialStateMarked = false;
403     }
404 
405     public boolean initialStateMarked()
406     {
407         return _initialStateMarked;
408     }
409 
410     public void markInitialState()
411     {
412         _initialStateMarked = true;
413     }
414 }