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.SimpleDateFormat;
23  import java.util.Locale;
24  import java.util.TimeZone;
25  
26  import javax.faces.component.StateHolder;
27  import javax.faces.component.UIComponent;
28  import javax.faces.context.FacesContext;
29  /**
30   * This tag associates a date time converter with the nearest parent UIComponent.
31   * 
32   * Unless otherwise specified, all attributes accept static values or EL expressions.
33   * 
34   * see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a>
35   *
36   * @JSFConverter
37   *   name="f:convertDateTime"
38   *   bodyContent="empty"
39   *   tagClass="org.apache.myfaces.taglib.core.ConvertDateTimeTag"
40   *
41   * @author Thomas Spiegl (latest modification by $Author: skitching $)
42   * @version $Revision: 676278 $ $Date: 2008-07-13 03:35:04 -0500 (Sun, 13 Jul 2008) $
43   */
44  public class DateTimeConverter implements Converter, StateHolder {
45      // API field
46      public static final String CONVERTER_ID = "javax.faces.DateTime";
47  
48      // internal constants
49      private static final String CONVERSION_MESSAGE_ID = "javax.faces.convert.DateTimeConverter.CONVERSION";
50  
51      private static final TimeZone TIMEZONE_DEFAULT = TimeZone.getTimeZone("GMT");
52  
53      private String _dateStyle;
54      private Locale _locale;
55      private String _pattern;
56      private String _timeStyle;
57      private TimeZone _timeZone;
58      private String _type;
59      private boolean _transient;
60  
61      public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
62          if (context == null) {
63              throw new NullPointerException("facesContext");
64          }
65          if (component == null) {
66              throw new NullPointerException("uiComponent");
67          }
68  
69          if (value == null) {
70              return null;
71          }
72  
73          String trimmedValue = value.trim();
74          if (trimmedValue.length() == 0) {
75              return null;
76          }
77  
78          try {
79              return prepareDateFormat().parse(trimmedValue);
80          } catch (Exception e) {
81              throw new ConverterException(
82                _MessageUtils.getErrorMessage(context, CONVERSION_MESSAGE_ID,
83                        new Object[] {value, component.getId()}), e);
84          }
85      }
86  
87      public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
88          if (context == null) {
89              throw new NullPointerException("facesContext");
90          }
91          if (component == null) {
92              throw new NullPointerException("uiComponent");
93          }
94  
95          if (value == null) {
96              return "";
97          }
98          if (value instanceof String) {
99              return (String) value;
100         }
101 
102         try {
103             return prepareDateFormat().format(value);
104         } catch (Exception e) {
105             throw new ConverterException(e);
106         }
107     }
108 
109     private DateFormat prepareDateFormat() {
110         DateFormat format = getDateFormat();
111         // format cannot be lenient (JSR-127)
112         format.setLenient(false);
113 
114         TimeZone tz = getTimeZone();
115         if (tz != null) {
116             format.setTimeZone(tz);
117         }
118 
119         return format;
120     }
121 
122     private DateFormat getDateFormat() {
123         if(_pattern != null) {
124             try {
125                 return new SimpleDateFormat(_pattern, getLocale());
126             } catch (IllegalArgumentException iae) {
127                 throw new ConverterException("Invalid pattern", iae);
128             }
129         }
130 
131         return Type.getType(getType()).getFormatter(calcDateStyle(), calcTimeStyle(), getLocale());
132     }
133 
134     private int calcDateStyle() {
135         return Style.getStyleFormat(getDateStyle());
136     }
137 
138     private int calcTimeStyle() {
139         return Style.getStyleFormat(getTimeStyle());
140     }
141 
142     // STATE SAVE/RESTORE
143     public void restoreState(FacesContext facesContext, Object state) {
144         Object[] values = (Object[]) state;
145         _dateStyle = (String) values[0];
146         _locale = (Locale) values[1];
147         _pattern = (String) values[2];
148         _timeStyle = (String) values[3];
149         _timeZone = (TimeZone) values[4];
150         _type = (String) values[5];
151     }
152 
153     public Object saveState(FacesContext facesContext) {
154         Object[] values = new Object[6];
155         values[0] = _dateStyle;
156         values[1] = _locale;
157         values[2] = _pattern;
158         values[3] = _timeStyle;
159         values[4] = _timeZone;
160         values[5] = _type;
161         return values;
162     }
163 
164     // GETTER & SETTER
165     
166     /**
167      * The style of the date.  Values include: default, short, medium, 
168      * long, and full.
169      * 
170      * @JSFProperty
171      */
172     public String getDateStyle() {
173         return _dateStyle != null ? _dateStyle : Style.DEFAULT.getName();
174     }
175 
176     public void setDateStyle(String dateStyle) {
177         //TODO: validate timeStyle
178         _dateStyle = dateStyle;
179     }
180 
181     /**
182      * The name of the locale to be used, instead of the default.
183      * 
184      * @JSFProperty
185      */
186     public Locale getLocale() {
187         if (_locale != null)
188             return _locale;
189         FacesContext context = FacesContext.getCurrentInstance();
190         return context.getViewRoot().getLocale();
191     }
192 
193     public void setLocale(Locale locale) {
194         _locale = locale;
195     }
196 
197     /**
198      * A custom Date formatting pattern, in the format used by java.text.SimpleDateFormat.
199      * 
200      * @JSFProperty
201      */
202     public String getPattern() {
203         return _pattern;
204     }
205 
206     public void setPattern(String pattern) {
207         _pattern = pattern;
208     }
209 
210     /**
211      * The style of the time.  Values include:  default, short, medium, long, 
212      * and full.
213      * 
214      * @JSFProperty
215      */
216     public String getTimeStyle() {
217         return _timeStyle != null ? _timeStyle : Style.DEFAULT.getName();
218     }
219 
220     public void setTimeStyle(String timeStyle) {
221         //TODO: validate timeStyle
222         _timeStyle = timeStyle;
223     }
224 
225     /**
226      * The time zone to use instead of GMT (the default timezone). When
227      * this value is a value-binding to a TimeZone instance, that
228      * timezone is used. Otherwise this value is treated as a String
229      * containing a timezone id, ie as the ID parameter of method
230      * java.util.TimeZone.getTimeZone(String).
231      * 
232      * @JSFProperty
233      */
234     public TimeZone getTimeZone() {
235         return _timeZone != null ? _timeZone : TIMEZONE_DEFAULT;
236     }
237 
238     public void setTimeZone(TimeZone timeZone) {
239         _timeZone = timeZone;
240     }
241 
242     public boolean isTransient() {
243         return _transient;
244     }
245 
246     public void setTransient(boolean aTransient) {
247         _transient = aTransient;
248     }
249 
250     /**
251      * Specifies whether the date, time, or both should be 
252      * parsed/formatted.  Values include:  date, time, and both.
253      * Default based on setting of timeStyle and dateStyle.
254      * 
255      * @JSFProperty
256      */
257     public String getType() {
258         return _type != null ? _type : Type.DATE.getName();
259     }
260 
261     public void setType(String type) {
262         //TODO: validate type
263         _type = type;
264     }
265 
266     private static class Style {
267 
268         private static final Style DEFAULT = new Style("default", DateFormat.DEFAULT);
269         private static final Style MEDIUM = new Style("medium", DateFormat.MEDIUM);
270         private static final Style SHORT = new Style("short", DateFormat.SHORT);
271         private static final Style LONG = new Style("long", DateFormat.LONG);
272         private static final Style FULL = new Style("full", DateFormat.FULL);
273 
274         private static final Style[] values = new Style[] {DEFAULT, MEDIUM, SHORT, LONG, FULL};
275 
276         public static Style getStyle(String name) {
277             for(int i = 0;i < values.length;i++) {
278                 if(values[i]._name.equals(name)) {
279                     return values[i];
280                 }
281             }
282 
283             throw new ConverterException("invalid style '" + name + "'");
284         }
285 
286         private static int getStyleFormat(String name) {
287             return getStyle(name).getFormat();
288         }
289 
290         private String _name;
291         private int _format;
292 
293         private Style(String name, int format) {
294             this._name = name;
295             this._format = format;
296         }
297 
298         public String getName() {
299             return _name;
300         }
301 
302         public int getFormat() {
303             return _format;
304         }
305     }
306 
307     private abstract static class Type {
308 
309         private static final Type DATE = new Type("date") {
310             public DateFormat getFormatter(int dateStyle, int timeStyle, Locale locale) {
311                 return DateFormat.getDateInstance(dateStyle, locale);
312             }
313         };
314         private static final Type TIME = new Type("time") {
315             public DateFormat getFormatter(int dateStyle, int timeStyle, Locale locale) {
316                 return DateFormat.getTimeInstance(timeStyle, locale);
317             }
318         };
319         private static final Type BOTH = new Type("both") {
320             public DateFormat getFormatter(int dateStyle, int timeStyle, Locale locale) {
321                 return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
322             }
323         };
324 
325         private static final Type[] values = new Type[] {DATE, TIME, BOTH};
326 
327         public static Type getType(String name) {
328             for(int i = 0;i < values.length;i++) {
329                 if(values[i]._name.equals(name)) {
330                     return values[i];
331                 }
332             }
333 
334             throw new ConverterException("invalid type '" + name + "'");
335         }
336 
337         private String _name;
338 
339         private Type(String name) {
340             this._name = name;
341         }
342 
343         public String getName() {
344             return _name;
345         }
346 
347         public abstract DateFormat getFormatter(int dateStyle, int timeStyle, Locale locale);
348     }
349 }