1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.struts.faces.renderer;
18
19
20 import java.io.IOException;
21 import java.util.Iterator;
22 import java.util.Map;
23
24 import javax.faces.application.FacesMessage;
25 import javax.faces.component.EditableValueHolder;
26 import javax.faces.component.UIComponent;
27 import javax.faces.component.ValueHolder;
28 import javax.faces.context.FacesContext;
29 import javax.faces.context.ResponseWriter;
30 import javax.faces.convert.Converter;
31 import javax.faces.convert.ConverterException;
32 import javax.faces.el.ValueBinding;
33 import javax.faces.render.Renderer;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38
39 /***
40 * <p>Abstract base class for concrete implementations of
41 * <code>javax.faces.render.Renderer</code> for the
42 * <em>Struts-Faces Integration Library</em>.</p>
43 *
44 * @version $Rev: 421138 $ $Date: 2006-07-11 22:41:40 -0700 (Tue, 11 Jul 2006) $
45 */
46
47 public abstract class AbstractRenderer extends Renderer {
48
49
50
51
52
53 private static final Log log =
54 LogFactory.getLog(AbstractRenderer.class);
55
56
57
58
59
60 /***
61 * <p>Decode any new state of the specified <code>UIComponent</code>
62 * from the request contained in the specified <code>FacesContext</code>,
63 * and store that state on the <code>UIComponent</code>.</p>
64 *
65 * <p>The default implementation calls <code>setSubmittedValue()</code>
66 * unless this component has a boolean <code>disabled</code> or
67 * <code>readonly</code> attribute that is set to <code>true</code>.</p>
68 *
69 * @param context <code>FacesContext</code> for the current request
70 * @param component <code>UIComponent</code> to be decoded
71 *
72 * @exception NullPointerException if <code>context</code> or
73 * <code>component</code> is <code>null</code>
74 */
75 public void decode(FacesContext context, UIComponent component) {
76
77
78 if ((context == null) || (component == null)) {
79 throw new NullPointerException();
80 }
81
82
83 if (isDisabled(component) || isReadOnly(component)) {
84 return;
85 }
86
87
88 if (component instanceof EditableValueHolder) {
89 setSubmittedValue(context, component);
90 }
91
92 }
93
94
95 /***
96 * <p>Render the beginning of the specified <code>UIComponent</code>
97 * to the output stream or writer associated with the response we are
98 * creating.</p>
99 *
100 * <p>The default implementation calls <code>renderStart()</code> and
101 * <code>renderAttributes()</code>.</p>
102 *
103 * @param context <code>FacesContext</code> for the current request
104 * @param component <code>UIComponent</code> to be decoded
105 *
106 * @exception NullPointerException if <code>context</code> or
107 * <code>component</code> is <code>null</code>
108 *
109 * @exception IOException if an input/output error occurs
110 */
111 public void encodeBegin(FacesContext context, UIComponent component)
112 throws IOException {
113
114
115 if ((context == null) || (component == null)) {
116 throw new NullPointerException();
117 }
118
119 if (log.isTraceEnabled()) {
120 log.trace("encodeBegin(id=" + component.getId() +
121 ", family=" + component.getFamily() +
122 ", rendererType=" + component.getRendererType() + ")");
123 }
124
125
126 ResponseWriter writer = context.getResponseWriter();
127 renderStart(context, component, writer);
128 renderAttributes(context, component, writer);
129
130 }
131
132
133 /***
134 * <p>Render the children of the specified <code>UIComponent</code>
135 * to the output stream or writer associated with the response we are
136 * creating.</p>
137 *
138 * <p>The default implementation iterates through the children of
139 * this component and renders them.</p>
140 *
141 * @param context <code>FacesContext</code> for the current request
142 * @param component <code>UIComponent</code> to be decoded
143 *
144 * @exception NullPointerException if <code>context</code> or
145 * <code>component</code> is <code>null</code>
146 *
147 * @exception IOException if an input/output error occurs
148 */
149 public void encodeChildren(FacesContext context, UIComponent component)
150 throws IOException {
151
152 if (context == null || component == null) {
153 throw new NullPointerException();
154 }
155
156 if (log.isTraceEnabled()) {
157 log.trace("encodeChildren(id=" + component.getId() +
158 ", family=" + component.getFamily() +
159 ", rendererType=" + component.getRendererType() + ")");
160 }
161 Iterator kids = component.getChildren().iterator();
162 while (kids.hasNext()) {
163 UIComponent kid = (UIComponent) kids.next();
164 kid.encodeBegin(context);
165 if (kid.getRendersChildren()) {
166 kid.encodeChildren(context);
167 }
168 kid.encodeEnd(context);
169 }
170 if (log.isTraceEnabled()) {
171 log.trace("encodeChildren(id=" + component.getId() + ") end");
172 }
173
174 }
175
176
177 /***
178 * <p>Render the ending of the specified <code>UIComponent</code>
179 * to the output stream or writer associated with the response we are
180 * creating.</p>
181 *
182 * <p>The default implementation calls <code>renderEnd()</code>.</p>
183 *
184 * @param context <code>FacesContext</code> for the current request
185 * @param component <code>UIComponent</code> to be decoded
186 *
187 * @exception NullPointerException if <code>context</code> or
188 * <code>component</code> is <code>null</code>
189 *
190 * @exception IOException if an input/output error occurs
191 */
192 public void encodeEnd(FacesContext context, UIComponent component)
193 throws IOException {
194
195
196 if ((context == null) || (component == null)) {
197 throw new NullPointerException();
198 }
199
200 if (log.isTraceEnabled()) {
201 log.trace("encodeEnd(id=" + component.getId() +
202 ", family=" + component.getFamily() +
203 ", rendererType=" + component.getRendererType() + ")");
204 }
205
206
207 ResponseWriter writer = context.getResponseWriter();
208 renderEnd(context, component, writer);
209
210 }
211
212
213
214
215
216
217
218
219 /***
220 * <p>Render nested child components by invoking the encode methods
221 * on those components, but only when the <code>rendered</code>
222 * property is <code>true</code>.</p>
223 */
224 protected void encodeRecursive(FacesContext context, UIComponent component)
225 throws IOException {
226
227
228
229 if (!component.isRendered()) {
230 return;
231 }
232
233
234 if (log.isTraceEnabled()) {
235 log.trace("encodeRecursive(id=" + component.getId() +
236 ", family=" + component.getFamily() +
237 ", rendererType=" + component.getRendererType() +
238 ") encodeBegin");
239 }
240 component.encodeBegin(context);
241 if (component.getRendersChildren()) {
242 if (log.isTraceEnabled()) {
243 log.trace("encodeRecursive(id=" + component.getId() +
244 ") delegating");
245 }
246 component.encodeChildren(context);
247 } else {
248 if (log.isTraceEnabled()) {
249 log.trace("encodeRecursive(id=" + component.getId() +
250 ") recursing");
251 }
252 Iterator kids = component.getChildren().iterator();
253 while (kids.hasNext()) {
254 UIComponent kid = (UIComponent) kids.next();
255 encodeRecursive(context, kid);
256 }
257 }
258 if (log.isTraceEnabled()) {
259 log.trace("encodeRecursive(id=" + component.getId() + ") encodeEnd");
260 }
261 component.encodeEnd(context);
262
263 }
264
265
266 /***
267 * <p>Return <code>true</code> if the specified component is disabled.</p>
268 *
269 * @param component <code>UIComponent</code> to be checked
270 */
271 protected boolean isDisabled(UIComponent component) {
272
273 Object disabled = component.getAttributes().get("disabled");
274 if (disabled == null) {
275 return (false);
276 }
277 if (disabled instanceof String) {
278 return (Boolean.valueOf((String) disabled).booleanValue());
279 } else {
280 return (disabled.equals(Boolean.TRUE));
281 }
282
283 }
284
285
286 /***
287 * <p>Return <code>true</code> if the specified component is read only.</p>
288 *
289 * @param component <code>UIComponent</code> to be checked
290 */
291 protected boolean isReadOnly(UIComponent component) {
292
293 Object readonly = component.getAttributes().get("readonly");
294 if (readonly == null) {
295 return (false);
296 }
297 if (readonly instanceof String) {
298 return (Boolean.valueOf((String) readonly).booleanValue());
299 } else {
300 return (readonly.equals(Boolean.TRUE));
301 }
302
303 }
304
305
306 /***
307 * <p>Render the element attributes for the generated markup related to this
308 * component. Simple renderers that create a single markup element
309 * for this component should override this method and include calls to
310 * to <code>writeAttribute()</code> and <code>writeURIAttribute</code>
311 * on the specified <code>ResponseWriter</code>.</p>
312 *
313 * <p>The default implementation does nothing.</p>
314 *
315 * @param context <code>FacesContext</code> for the current request
316 * @param component <code>EditableValueHolder</code> component whose
317 * submitted value is to be stored
318 * @param writer <code>ResponseWriter</code> to which the element
319 * start should be rendered
320 *
321 * @exception IOException if an input/output error occurs
322 */
323 protected void renderAttributes(FacesContext context, UIComponent component,
324 ResponseWriter writer) throws IOException {
325
326 }
327
328
329 /***
330 * <p>Render the element end for the generated markup related to this
331 * component. Simple renderers that create a single markup element
332 * for this component should override this method and include a call
333 * to <code>endElement()</code> on the specified
334 * <code>ResponseWriter</code>.</p>
335 *
336 * <p>The default implementation does nothing.</p>
337 *
338 * @param context <code>FacesContext</code> for the current request
339 * @param component <code>EditableValueHolder</code> component whose
340 * submitted value is to be stored
341 * @param writer <code>ResponseWriter</code> to which the element
342 * start should be rendered
343 *
344 * @exception IOException if an input/output error occurs
345 */
346 protected void renderEnd(FacesContext context, UIComponent component,
347 ResponseWriter writer) throws IOException {
348
349 }
350
351
352 /***
353 * <p>Render any boolean attributes on the specified list that have
354 * <code>true</code> values on the corresponding attribute of the
355 * specified <code>UIComponent</code>.</p>
356 *
357 * @param context <code>FacesContext</code> for the current request
358 * @param component <code>EditableValueHolder</code> component whose
359 * submitted value is to be stored
360 * @param writer <code>ResponseWriter</code> to which the element
361 * start should be rendered
362 * @param names List of attribute names to be passed through
363 *
364 * @exception IOException if an input/output error occurs
365 */
366 protected void renderBoolean(FacesContext context,
367 UIComponent component,
368 ResponseWriter writer,
369 String names[]) throws IOException {
370
371 if (names == null) {
372 return;
373 }
374 Map attributes = component.getAttributes();
375 boolean flag;
376 Object value;
377 for (int i = 0; i < names.length; i++) {
378 value = attributes.get(names[i]);
379 if (value != null) {
380 if (value instanceof String) {
381 flag = Boolean.valueOf((String) value).booleanValue();
382 } else {
383 flag = Boolean.valueOf(value.toString()).booleanValue();
384 }
385 if (flag) {
386 writer.writeAttribute(names[i], names[i], names[i]);
387 flag = false;
388 }
389 }
390 }
391
392 }
393
394
395 /***
396 * <p>Render any attributes on the specified list directly to the
397 * specified <code>ResponseWriter</code> for which the specified
398 * <code>UIComponent</code> has a non-<code>null</code> attribute value.
399 * This method may be used to "pass through" commonly used attribute
400 * name/value pairs with a minimum of code.</p>
401 *
402 * @param context <code>FacesContext</code> for the current request
403 * @param component <code>EditableValueHolder</code> component whose
404 * submitted value is to be stored
405 * @param writer <code>ResponseWriter</code> to which the element
406 * start should be rendered
407 * @param names List of attribute names to be passed through
408 *
409 * @exception IOException if an input/output error occurs
410 */
411 protected void renderPassThrough(FacesContext context,
412 UIComponent component,
413 ResponseWriter writer,
414 String names[]) throws IOException {
415
416 if (names == null) {
417 return;
418 }
419 Map attributes = component.getAttributes();
420 Object value;
421 for (int i = 0; i < names.length; i++) {
422 value = attributes.get(names[i]);
423 if (value != null) {
424 if (value instanceof String) {
425 writer.writeAttribute(names[i], value, names[i]);
426 } else {
427 writer.writeAttribute(names[i], value.toString(), names[i]);
428 }
429 }
430 }
431
432 }
433
434
435 /***
436 * <p>Render the element start for the generated markup related to this
437 * component. Simple renderers that create a single markup element
438 * for this component should override this method and include a call
439 * to <code>startElement()</code> on the specified
440 * <code>ResponseWriter</code>.</p>
441 *
442 * <p>The default implementation does nothing.</p>
443 *
444 * @param context <code>FacesContext</code> for the current request
445 * @param component <code>EditableValueHolder</code> component whose
446 * submitted value is to be stored
447 * @param writer <code>ResponseWriter</code> to which the element
448 * start should be rendered
449 *
450 * @exception IOException if an input/output error occurs
451 */
452 protected void renderStart(FacesContext context, UIComponent component,
453 ResponseWriter writer) throws IOException {
454
455 }
456
457
458 /***
459 * <p>If a submitted value was included on this request, store it in the
460 * component as appropriate.</p>
461 *
462 * <p>The default implementation determines whether this component
463 * implements <code>EditableValueHolder</code>. If so, it checks for a
464 * request parameter with the same name as the <code>clientId</code>
465 * of this <code>UIComponent</code>. If there is such a parameter, its
466 * value is passed (as a String) to the <code>setSubmittedValue()</code>
467 * method on the <code>EditableValueHolder</code> component.</p>
468 *
469 * @param context <code>FacesContext</code> for the current request
470 * @param component <code>EditableValueHolder</code> component whose
471 * submitted value is to be stored
472 */
473 protected void setSubmittedValue
474 (FacesContext context, UIComponent component) {
475
476 if (!(component instanceof EditableValueHolder)) {
477 return;
478 }
479 String clientId = component.getClientId(context);
480 Map parameters = context.getExternalContext().getRequestParameterMap();
481 if (parameters.containsKey(clientId)) {
482 if (log.isTraceEnabled()) {
483 log.trace("setSubmittedValue(" + clientId + "," +
484 (String) parameters.get(clientId));
485 }
486 component.getAttributes().put("submittedValue",
487 parameters.get(clientId));
488 }
489
490 }
491
492
493
494
495
496 /***
497 * <p>Decode the current state of the specified UIComponent from the
498 * request contained in the specified <code>FacesContext</code>, and
499 * attempt to convert this state information into an object of the
500 * type equired for this component.</p>
501 *
502 * @param context FacesContext for the request we are processing
503 * @param component UIComponent to be decoded
504 *
505 * @exception NullPointerException if context or component is null
506 */
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 /***
558 * <p>Add an error message denoting a conversion failure.</p>
559 *
560 * @param context The <code>FacesContext</code> for this request
561 * @param component The <code>UIComponent</code> that experienced
562 * the conversion failure
563 * @param text The text of the error message
564 */
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580 /***
581 * <p>Convert the String representation of this component's value
582 * to the corresponding Object representation. The default
583 * implementation utilizes the <code>getAsObject()</code> method of any
584 * associated <code>Converter</code>.</p>
585 *
586 * @param context The <code>FacesContext</code> for this request
587 * @param component The <code>UIComponent</code> whose value is
588 * being converted
589 * @param value The String representation to be converted
590 *
591 * @exception ConverterException if conversion fails
592 */
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624 /***
625 * <p>Convert the Object representation of this component's value
626 * to the corresponding String representation. The default implementation
627 * utilizes the <code>getAsString()</code> method of any associated
628 * <code>Converter</code>.</p>
629 *
630 * @param context The <code>FacesContext</code> for this request
631 * @param component The <code>UIComponent</code> whose value is
632 * being converted
633 * @param value The Object representation to be converted
634 *
635 * @exception ConverterException if conversion fails
636 */
637 protected String getAsString(FacesContext context, UIComponent component,
638 Object value) throws ConverterException {
639
640
641 ValueBinding vb = component.getValueBinding("value");
642 Converter converter = null;
643 if (component instanceof ValueHolder) {
644
645 converter = ((ValueHolder) component).getConverter();
646 }
647 if ((converter == null) && (vb != null)) {
648
649 Class type = vb.getType(context);
650 if (type != null) {
651 converter = context.getApplication().createConverter(type);
652 }
653 }
654
655
656 if (converter != null) {
657 return (converter.getAsString(context, component, value));
658 } else if (value == null) {
659 return ("");
660 } else if (value instanceof String) {
661 return ((String) value);
662 } else {
663 return (value.toString());
664 }
665
666 }
667
668
669 }