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.Date;
26  
27  import javax.el.MethodExpression;
28  
29  import javax.faces.component.UIComponent;
30  import javax.faces.component.UIViewRoot;
31  import javax.faces.el.MethodBinding;
32  import javax.faces.el.ValueBinding;
33  import javax.faces.webapp.UIComponentTag;
34  
35  import javax.servlet.jsp.JspException;
36  import javax.servlet.jsp.tagext.TagSupport;
37  
38  import org.apache.myfaces.trinidad.bean.FacesBean;
39  import org.apache.myfaces.trinidad.bean.PropertyKey;
40  import org.apache.myfaces.trinidad.component.UIXComponent;
41  import org.apache.myfaces.trinidad.event.AttributeChangeEvent;
42  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
43  
44  
45  /**
46   * Subclass of UIComponentTag to add convenience methods,
47   * and optimize where appropriate.
48   */
49  @SuppressWarnings("deprecation")
50  abstract public class UIXComponentTag extends UIComponentTag
51  {
52    public UIXComponentTag()
53    {
54    }
55  
56    @Override
57    public void setId(String id)
58    {
59      _id = id;
60    }
61  
62    @Override
63    public void setRendered(String rendered)
64    {
65      _rendered = rendered;
66    }
67  
68    public void setAttributeChangeListener(String attributeChangeListener)
69    {
70      _attributeChangeListener = attributeChangeListener;
71    }
72  
73    @Override
74    public int doStartTag() throws JspException
75    {
76      _parentELContext = (ELContextTag)
77         TagSupport.findAncestorWithClass(this, ELContextTag.class);
78  
79      // Transform "rendered" on behalf of the UIComponentTag
80      String rendered = _rendered;
81      if (rendered != null)
82      {
83        if ((_parentELContext != null) && isValueReference(rendered))
84          rendered = _parentELContext.transformExpression(rendered);
85  
86        super.setRendered(rendered);
87      }
88  
89  
90      String id = _id;
91      if (id != null)
92      {
93        if (_parentELContext != null)
94          id = _parentELContext.transformId(id);
95  
96        super.setId(id);
97      }
98  
99      int retVal = super.doStartTag();
100 
101     //pu: There could have been some validation error during property setting
102     //  on the bean, this is the closest opportunity to burst out.
103     if (_validationError != null)
104       throw new JspException(_validationError);
105 
106     return retVal;
107   }
108 
109   @Override
110   protected final void setProperties(UIComponent component)
111   {
112     if (component instanceof UIViewRoot)
113     {
114       throw new IllegalStateException(_LOG.getMessage(
115         "VIEW_TAG_NOT_PRESENT", this));
116     }
117 
118     super.setProperties(component);
119 
120     UIXComponent uixComponent = (UIXComponent) component;
121 
122     if (_attributeChangeListener != null)
123     {
124       MethodExpression me = getFacesContext().getApplication().
125          getExpressionFactory().createMethodExpression(
126              getFacesContext().getELContext(),
127              _attributeChangeListener,
128              null,
129              new Class[]{AttributeChangeEvent.class});
130 
131       uixComponent.setAttributeChangeListener(me);
132     }
133 
134     setProperties(uixComponent.getFacesBean());
135   }
136 
137   protected void setProperty(
138     FacesBean   bean,
139     PropertyKey key,
140     String      value)
141   {
142     if (value == null)
143       return;
144 
145     if (isValueReference(value))
146     {
147       bean.setValueBinding(key, createValueBinding(value));
148     }
149     else
150     {
151       bean.setProperty(key, value);
152     }
153   }
154 
155   /**
156    * Set a property of type java.lang.Boolean.  If the value
157    * is an EL expression, it will be stored as a ValueBinding.
158    * Otherwise, it will parsed with Integer.valueOf().
159    * Null values are ignored.
160    */
161   protected void setBooleanProperty(
162     FacesBean   bean,
163     PropertyKey key,
164     String      value)
165   {
166     if (value == null)
167       return;
168 
169     if (isValueReference(value))
170     {
171       bean.setValueBinding(key, createValueBinding(value));
172     }
173     else
174     {
175       bean.setProperty(key, Boolean.valueOf(value));
176     }
177   }
178 
179   /**
180    * Set a property of type java.lang.Number.  If the value
181    * is an EL expression, it will be stored as a ValueBinding.
182    * Otherwise, it will parsed with Integer.valueOf() or Double.valueOf() .
183    * Null values are ignored.
184    */
185   protected void setNumberProperty(
186     FacesBean   bean,
187     PropertyKey key,
188     String      value)
189   {
190     if (value == null)
191       return;
192 
193     if (isValueReference(value))
194     {
195       bean.setValueBinding(key, createValueBinding(value));
196     }
197     else
198     {
199       if(value.indexOf('.') == -1)
200         bean.setProperty(key, Integer.valueOf(value));
201       else
202         bean.setProperty(key, Double.valueOf(value));
203     }
204   }
205  /**
206    * Set a property of type java.lang.Integer.  If the value
207    * is an EL expression, it will be stored as a ValueBinding.
208    * Otherwise, it will parsed with Integer.valueOf().
209    * Null values are ignored.
210    */
211   protected void setIntegerProperty(
212    FacesBean   bean,
213    PropertyKey key,
214    String      value)
215  {
216    if (value == null)
217      return;
218 
219    if (isValueReference(value))
220    {
221      bean.setValueBinding(key, createValueBinding(value));
222    }
223    else
224    {
225      bean.setProperty(key, Integer.valueOf(value));
226    }
227  }
228 
229 
230   /**
231    * Set a property of type java.lang.Character.  If the value
232    * is an EL expression, it will be stored as a ValueBinding.
233    * Otherwise, its first character will be stored (unless
234    * it is an empty string, in which case it will be ignored).
235    * Null values are ignored.
236    */
237   protected void setCharacterProperty(
238     FacesBean   bean,
239     PropertyKey key,
240     String      value)
241   {
242     if (value == null)
243       return;
244 
245     if (isValueReference(value))
246     {
247       bean.setValueBinding(key, createValueBinding(value));
248     }
249     else
250     {
251       if (value.length() >= 1)
252         bean.setProperty(key, Character.valueOf(value.charAt(0)));
253     }
254   }
255 
256   /**
257    * Set a property of type java.lang.Long.  If the value
258    * is an EL expression, it will be stored as a ValueBinding.
259    * Otherwise, it will parsed with Long.valueOf().
260    * Null values are ignored.
261    */
262   protected void setLongProperty(
263     FacesBean   bean,
264     PropertyKey key,
265     String      value)
266   {
267     if (value == null)
268       return;
269 
270     if (isValueReference(value))
271     {
272       bean.setValueBinding(key, createValueBinding(value));
273     }
274     else
275     {
276       bean.setProperty(key, Long.valueOf(value));
277     }
278   }
279 
280   /**
281    * Set a property of type java.lang.Double.  If the value
282    * is an EL expression, it will be stored as a ValueBinding.
283    * Otherwise, it will parsed with Double.valueOf().
284    * Null values are ignored.
285    */
286   protected void setDoubleProperty(
287     FacesBean   bean,
288     PropertyKey key,
289     String      value)
290   {
291     if (value == null)
292       return;
293 
294     if (isValueReference(value))
295     {
296       bean.setValueBinding(key, createValueBinding(value));
297     }
298     else
299     {
300       bean.setProperty(key, Double.valueOf(value));
301     }
302   }
303 
304   /**
305    * Set a property of type java.lang.Float.  If the value
306    * is an EL expression, it will be stored as a ValueBinding.
307    * Otherwise, it will parsed with Float.valueOf().
308    * Null values are ignored.
309    */
310   protected void setFloatProperty(
311     FacesBean   bean,
312     PropertyKey key,
313     String      value)
314   {
315     if (value == null)
316       return;
317 
318     if (isValueReference(value))
319     {
320       bean.setValueBinding(key, createValueBinding(value));
321     }
322     else
323     {
324       bean.setProperty(key, Float.valueOf(value));
325     }
326   }
327 
328 
329 
330   /**
331    * Set a property of type java.lang.String[].  If the value
332    * is an EL expression, it will be stored as a ValueBinding.
333    * Otherwise, it will parsed as a whitespace-separated series
334    * of strings.
335    * Null values are ignored.
336    */
337   protected void setStringArrayProperty(
338     FacesBean   bean,
339     PropertyKey key,
340     String      value)
341   {
342     if (value == null)
343       return;
344 
345     if (isValueReference(value))
346     {
347       bean.setValueBinding(key, createValueBinding(value));
348     }
349     else
350     {
351       bean.setProperty(key, TagUtils.parseNameTokens(value));
352     }
353   }
354 
355   /**
356    * Set a property of type int[].  If the value
357    * is an EL expression, it will be stored as a ValueBinding.
358    * Otherwise, it will parsed as a whitespace-separated series
359    * of ints.
360    * Null values are ignored.
361    */
362   protected void setIntArrayProperty(
363     FacesBean   bean,
364     PropertyKey key,
365     String      value)
366   {
367     if (value == null)
368       return;
369 
370     if (isValueReference(value))
371     {
372       bean.setValueBinding(key, createValueBinding(value));
373     }
374     else
375     {
376       String[] strings = TagUtils.parseNameTokens(value);
377       final int[] ints;
378       if (strings != null)
379       {
380         try
381         {
382           ints = new int[strings.length];
383           for(int i=0; i<strings.length; i++)
384           {
385             int j = Integer.parseInt(strings[i]);
386             ints[i] = j;
387           }
388         }
389         catch (NumberFormatException e)
390         {
391           _LOG.severe("CANNOT_CONVERT_INTO_INT_ARRAY",value);
392           _LOG.severe(e);
393           return;
394         }
395       }
396       else
397         ints = null;
398 
399       bean.setProperty(key, ints);
400     }
401   }
402 
403   /**
404    * Set a property of type java.util.Date.  If the value
405    * is an EL expression, it will be stored as a ValueBinding.
406    * Otherwise, it will parsed as an ISO 8601 date (yyyy-MM-dd).
407    * Null values are ignored.
408    */
409   protected void setDateProperty(
410     FacesBean   bean,
411     PropertyKey key,
412     String      value)
413   {
414     if (value == null)
415       return;
416 
417     if (isValueReference(value))
418     {
419       bean.setValueBinding(key, createValueBinding(value));
420     }
421     else
422     {
423       bean.setProperty(key, _parseISODate(value));
424     }
425   }
426 
427   // TODO Handle syntax exceptions gracefully?
428   protected final ValueBinding createValueBinding(String string)
429   {
430     if (_parentELContext != null)
431       string = _parentELContext.transformExpression(string);
432 
433     return getFacesContext().getApplication().createValueBinding(string);
434   }
435 
436   // TODO Handle syntax exceptions gracefully?
437   protected final MethodBinding createMethodBinding(
438     String   string,
439     Class [] types)
440   {
441     if (_parentELContext != null)
442       string = _parentELContext.transformExpression(string);
443 
444     return getFacesContext().getApplication().createMethodBinding(string,
445                                                                   types);
446   }
447 
448   protected void setProperties(
449     @SuppressWarnings("unused")
450     FacesBean bean)
451   {
452     // Could be abstract, but it's easier to *always* call super.setProperties(),
453     // and perhaps we'll have something generic in here, esp. if we take
454     // over "rendered" from UIComponentTag
455   }
456 
457   /**
458    * Sets any fatal validation error that could have happened during property
459    *  setting. If this is set, tag execution aborts with a JspException at the
460    *  end of doStartTag().
461    * @param validationError
462    */
463   protected void setValidationError(String validationError)
464   {
465     _validationError = validationError;
466   }
467 
468   /**
469    * Parse a string into a java.util.Date object.  The
470    * string must be in ISO 9601 format (yyyy-MM-dd).
471    */
472   static private final Date _parseISODate(String stringValue)
473   {
474     try
475     {
476       return _getDateFormat().parse(stringValue);
477     }
478     catch (ParseException pe)
479     {
480       _LOG.info("CANNOT_PARSE_VALUE_INTO_DATE", stringValue);
481       return null;
482     }
483   }
484 
485   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentTag.class);
486 
487   // We rely strictly on ISO 8601 formats
488   private static DateFormat _getDateFormat()
489   {
490     return new SimpleDateFormat("yyyy-MM-dd");
491   }
492 
493   private String       _rendered;
494   private String       _id;
495   private String       _attributeChangeListener;
496   private String       _validationError;
497   private ELContextTag _parentELContext;
498 }