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