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 org.apache.myfaces.trinidad.webapp;
20  
21  import java.text.DateFormat;
22  import java.text.ParseException;
23  import java.text.SimpleDateFormat;
24  
25  import java.util.ArrayList;
26  import java.util.Calendar;
27  import java.util.Date;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Set;
31  import java.util.TimeZone;
32  
33  import javax.el.MethodExpression;
34  import javax.el.ValueExpression;
35  
36  import javax.faces.component.UIComponent;
37  import javax.faces.component.UIViewRoot;
38  import javax.faces.webapp.UIComponentELTag;
39  
40  import javax.servlet.jsp.JspException;
41  
42  import org.apache.myfaces.trinidad.bean.FacesBean;
43  import org.apache.myfaces.trinidad.bean.PropertyKey;
44  import org.apache.myfaces.trinidad.component.UIXComponent;
45  import org.apache.myfaces.trinidad.context.RequestContext;
46  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
47  
48  
49  /**
50   * Subclass of UIComponentTag to add convenience methods,
51   * and optimize where appropriate.
52   */
53  abstract public class UIXComponentELTag extends UIComponentELTag
54  {
55    public UIXComponentELTag()
56    {
57    }
58  
59    public void setAttributeChangeListener(MethodExpression attributeChangeListener)
60    {
61      _attributeChangeListener = attributeChangeListener;
62    }
63  
64    @Override
65    public int doStartTag() throws JspException
66    {
67      int retVal = super.doStartTag();
68  
69      //pu: There could have been some validation error during property setting
70      //  on the bean, this is the closest opportunity to burst out.
71      if (_validationError != null)
72        throw new JspException(_validationError);
73  
74      return retVal;
75    }
76  
77    @Override
78    protected final void setProperties(UIComponent component)
79    {
80      if (component instanceof UIViewRoot)
81      {
82        throw new IllegalStateException(
83           "<f:view> was not present on this page; tag " + this +
84           "encountered without an <f:view> being processed.");
85      }
86  
87      super.setProperties(component);
88  
89      UIXComponent uixComponent = (UIXComponent) component;
90  
91      if (_attributeChangeListener != null)
92      {
93        uixComponent.setAttributeChangeListener(_attributeChangeListener);
94      }
95  
96      setProperties(uixComponent.getFacesBean());
97    }
98  
99    protected void setProperty(
100     FacesBean   bean,
101     PropertyKey key,
102     ValueExpression expression)
103   {
104     if (expression == null)
105       return;
106 
107     if (expression.isLiteralText())
108     {
109       bean.setProperty(key, expression.getValue(null));
110     }
111     else
112     {
113       bean.setValueExpression(key, expression);
114     }
115   }
116 
117   /**
118    * Set a property of type java.lang.String[].  If the value
119    * is an EL expression, it will be stored as a ValueExpression.
120    * Otherwise, it will parsed as a whitespace-separated series
121    * of strings.
122    * Null values are ignored.
123    */
124   protected void setStringArrayProperty(
125     FacesBean       bean,
126     PropertyKey     key,
127     ValueExpression expression)
128   {
129     if (expression == null)
130       return;
131 
132     if (expression.isLiteralText())
133     {
134       bean.setProperty(key, _parseNameTokens(expression.getValue(null)));
135     }
136     else
137     {
138       bean.setValueExpression(key, expression);
139     }
140   }
141 
142   /**
143    * Set a property of type java.util.List<java.lang.String>.  If the value
144    * is an EL expression, it will be stored as a ValueExpression.
145    * Otherwise, it will parsed as a whitespace-separated series
146    * of strings.
147    * Null values are ignored.
148    */
149   protected void setStringListProperty(
150     FacesBean       bean,
151     PropertyKey     key,
152     ValueExpression expression)
153   {
154     if (expression == null)
155       return;
156 
157     if (expression.isLiteralText())
158     {
159       bean.setProperty(key, 
160                        _parseNameTokensAsList(expression.getValue(null)));
161     }
162     else
163     {
164       bean.setValueExpression(key, expression);
165     }
166   }
167 
168   /**
169    * Set a property of type java.util.Set<java.lang.String>.  If the value
170    * is an EL expression, it will be stored as a ValueExpression.
171    * Otherwise, it will parsed as a whitespace-separated series
172    * of strings.
173    * Null values are ignored.
174    */
175   protected void setStringSetProperty(
176     FacesBean       bean,
177     PropertyKey     key,
178     ValueExpression expression)
179   {
180     if (expression == null)
181       return;
182 
183     if (expression.isLiteralText())
184     {
185       bean.setProperty(key, 
186                        _parseNameTokensAsSet(expression.getValue(null)));
187     }
188     else
189     {
190       bean.setValueExpression(key, expression);
191     }
192   }
193 
194   /**
195    * Set a property of type java.lang.Number.  If the value
196    * is an EL expression, it will be stored as a ValueBinding.
197    * Otherwise, it will parsed with Integer.valueOf() or Double.valueOf() .
198    * Null values are ignored.
199    */
200   protected void setNumberProperty(
201     FacesBean   bean,
202     PropertyKey key,
203     ValueExpression expression)
204   {
205     if (expression == null)
206       return;
207 
208     if (expression.isLiteralText())
209     {
210       Object value = expression.getValue(null);
211       if (value != null)
212       { 
213         if (value instanceof Number)
214         {
215           bean.setProperty(key, value);
216         }
217         else
218         {
219           String valueStr = value.toString();
220           if(valueStr.indexOf('.') == -1)
221             bean.setProperty(key, Integer.valueOf(valueStr));
222           else
223             bean.setProperty(key, Double.valueOf(valueStr));
224         }
225       }
226     }
227     else
228     {
229       bean.setValueExpression(key, expression);
230     }
231   }
232 
233   /**
234    * Set a property of type int[].  If the value
235    * is an EL expression, it will be stored as a ValueExpression.
236    * Otherwise, it will parsed as a whitespace-separated series
237    * of ints.
238    * Null values are ignored.
239    */
240   protected void setIntArrayProperty(
241     FacesBean   bean,
242     PropertyKey key,
243     ValueExpression expression)
244   {
245     if (expression == null)
246       return;
247 
248     if (expression.isLiteralText())
249     {
250       Object value = expression.getValue(null);
251       if (value != null)
252       {
253         String[] strings = _parseNameTokens(value);
254         final int[] ints;
255         if (strings != null)
256         {
257           try
258           {
259             ints = new int[strings.length];
260             for(int i=0; i<strings.length; i++)
261             {
262               int j = Integer.parseInt(strings[i]);
263               ints[i] = j;
264             }
265           }
266           catch (NumberFormatException e)
267           {
268             _LOG.severe("CANNOT_CONVERT_INTO_INT_ARRAY",value);
269             _LOG.severe(e);
270             return;
271           }
272         }
273       }
274     }
275     else
276     {
277       bean.setValueExpression(key, expression);
278     }
279   }
280 
281   /**
282    * Set a property of type java.util.Date.  If the value
283    * is an EL expression, it will be stored as a ValueExpression.
284    * Otherwise, it will parsed as an ISO 8601 date (yyyy-MM-dd).
285    * Null values are ignored.
286    */
287   protected void setDateProperty(
288     FacesBean   bean,
289     PropertyKey key,
290     ValueExpression expression)
291   {
292     if (expression == null)
293       return;
294 
295     if (expression.isLiteralText())
296     {
297       bean.setProperty(key, _parseISODate(expression.getValue(null)));
298     }
299     else
300     {
301       bean.setValueExpression(key, expression);
302     }
303   }
304 
305     /**
306    * Set a property of type java.util.Date.  If the value
307    * is an EL expression, it will be stored as a ValueBinding.
308    * Otherwise, it will parsed as an ISO 8601 date (yyyy-MM-dd)
309    * and the time components (hour, min, second, millisecond) maximized.
310    * Null values are ignored.
311    */
312     protected void setMaxDateProperty(
313     FacesBean   bean,
314     PropertyKey key,
315     ValueExpression expression)
316   {
317     if (expression == null)
318       return;
319 
320     if (expression.isLiteralText())
321     {
322       Date d = _parseISODate(expression.getValue(null));
323       Calendar c = Calendar.getInstance();
324       TimeZone tz = RequestContext.getCurrentInstance().getTimeZone();
325       if (tz != null)
326         c.setTimeZone(tz);
327       c.setTime(d);
328       // Original value had 00:00:00 for hours,mins, seconds now maximize those
329       // to get the latest time value for the date supplied.
330       c.set (Calendar.HOUR_OF_DAY, 23);
331       c.set (Calendar.MINUTE, 59);
332       c.set (Calendar.SECOND, 59);
333       c.set (Calendar.MILLISECOND, 999);
334       bean.setProperty(key, c.getTime());
335     }
336     else
337     {
338       bean.setValueExpression(key, expression);
339     }
340   }
341 
342   protected void setProperties(FacesBean bean)
343   {
344     // Could be abstract, but it's easier to *always* call super.setProperties(),
345     // and perhaps we'll have something generic in here, esp. if we take
346     // over "rendered" from UIComponentTag
347   }
348 
349   /**
350    * Sets any fatal validation error that could have happened during property
351    *  setting. If this is set, tag execution aborts with a JspException at the
352    *  end of doStartTag().
353    * @param validationError
354    */
355   protected void setValidationError(String validationError)
356   {
357     _validationError = validationError;
358   }
359 
360   /**
361    * Parse a string into a java.util.Date object.  The
362    * string must be in ISO 9601 format (yyyy-MM-dd).
363    */
364   static private final Date _parseISODate(Object o)
365   {
366     if (o == null)
367       return null;
368 
369     String stringValue = o.toString();
370     try
371     {
372       return _getDateFormat().parse(stringValue);
373     }
374     catch (ParseException pe)
375     {
376       _LOG.info("CANNOT_PARSE_VALUE_INTO_DATE", stringValue);
377       return null;
378     }
379   }
380 
381   /**
382    * Parses a whitespace separated series of name tokens.
383    * @param stringValue the full string
384    * @return an array of each constituent value, or null
385    *  if there are no tokens (that is, the string is empty or
386    *  all whitespace)
387    * @todo Move to utility function somewhere (ADF Share?)
388    */
389   static private final String[] _parseNameTokens(Object o)
390   {
391     List<String> list = _parseNameTokensAsList (o);
392 
393     if (list == null)
394       return null;
395 
396     return list.toArray(new String[list.size()]);
397   }
398 
399   static private final List<String> _parseNameTokensAsList (Object o)
400   {
401     if (o == null)
402       return null;
403 
404     String stringValue = o.toString();
405     ArrayList<String> list = new ArrayList<String>(5);
406 
407     int     length = stringValue.length();
408     boolean inSpace = true;
409     int     start = 0;
410     for (int i = 0; i < length; i++)
411     {
412       char ch = stringValue.charAt(i);
413 
414       // We're in whitespace;  if we've just departed
415       // a run of non-whitespace, append a string.
416       // Now, why do we use the supposedly deprecated "Character.isSpace()"
417       // function instead of "isWhitespace"?  We're following XML rules
418       // here for the meaning of whitespace, which specifically
419       // EXCLUDES general Unicode spaces.
420       if (Character.isWhitespace(ch))
421       {
422         if (!inSpace)
423         {
424           list.add(stringValue.substring(start, i));
425           inSpace = true;
426         }
427       }
428       // We're out of whitespace;  if we've just departed
429       // a run of whitespace, start keeping track of this string
430       else
431       {
432         if (inSpace)
433         {
434           start = i;
435           inSpace = false;
436         }
437       }
438     }
439 
440     if (!inSpace)
441       list.add(stringValue.substring(start));
442 
443     if (list.isEmpty())
444       return null;
445 
446     return list;
447   }
448 
449   static private final Set<String> _parseNameTokensAsSet (Object o)
450   {
451     List<String> list = _parseNameTokensAsList(o);
452 
453     if (list == null)
454       return null;
455     else
456       return new HashSet(list);
457   }
458 
459   private static final TrinidadLogger _LOG = 
460     TrinidadLogger.createTrinidadLogger(UIXComponentELTag.class);
461 
462   // We rely strictly on ISO 8601 formats
463   private static DateFormat _getDateFormat()
464   {
465     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
466     TimeZone tz = RequestContext.getCurrentInstance().getTimeZone();
467     if (tz != null)
468       sdf.setTimeZone(tz);
469     return sdf;
470   }
471 
472   //  No more used anywhere in Trinidad code, so deprecate since 2.0.x.
473   @Deprecated
474   public static final String DOCUMENT_CREATED_KEY = "org.apache.myfaces.trinidad.DOCUMENTCREATED";
475 
476   private MethodExpression  _attributeChangeListener;
477   private String            _validationError;
478   
479 }