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