001: /*
002: * $Id: AbstractRenderer.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.faces.renderer;
023:
024: import java.io.IOException;
025: import java.util.Iterator;
026: import java.util.Map;
027:
028: import javax.faces.application.FacesMessage;
029: import javax.faces.component.EditableValueHolder;
030: import javax.faces.component.UIComponent;
031: import javax.faces.component.ValueHolder;
032: import javax.faces.context.FacesContext;
033: import javax.faces.context.ResponseWriter;
034: import javax.faces.convert.Converter;
035: import javax.faces.convert.ConverterException;
036: import javax.faces.el.ValueBinding;
037: import javax.faces.render.Renderer;
038:
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041:
042: /**
043: * <p>Abstract base class for concrete implementations of
044: * <code>javax.faces.render.Renderer</code> for the
045: * <em>Struts-Faces Integration Library</em>.</p>
046: *
047: * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
048: */
049:
050: public abstract class AbstractRenderer extends Renderer {
051:
052: // -------------------------------------------------------- Static Variables
053:
054: private static final Log log = LogFactory
055: .getLog(AbstractRenderer.class);
056:
057: // -------------------------------------------------------- Renderer Methods
058:
059: /**
060: * <p>Decode any new state of the specified <code>UIComponent</code>
061: * from the request contained in the specified <code>FacesContext</code>,
062: * and store that state on the <code>UIComponent</code>.</p>
063: *
064: * <p>The default implementation calls <code>setSubmittedValue()</code>
065: * unless this component has a boolean <code>disabled</code> or
066: * <code>readonly</code> attribute that is set to <code>true</code>.</p>
067: *
068: * @param context <code>FacesContext</code> for the current request
069: * @param component <code>UIComponent</code> to be decoded
070: *
071: * @exception NullPointerException if <code>context</code> or
072: * <code>component</code> is <code>null</code>
073: */
074: public void decode(FacesContext context, UIComponent component) {
075:
076: // Enforce NPE requirements in the Javadocs
077: if ((context == null) || (component == null)) {
078: throw new NullPointerException();
079: }
080:
081: // Disabled or readonly components are not decoded
082: if (isDisabled(component) || isReadOnly(component)) {
083: return;
084: }
085:
086: // Save submitted value on EditableValueHolder components
087: if (component instanceof EditableValueHolder) {
088: setSubmittedValue(context, component);
089: }
090:
091: }
092:
093: /**
094: * <p>Render the beginning of the specified <code>UIComponent</code>
095: * to the output stream or writer associated with the response we are
096: * creating.</p>
097: *
098: * <p>The default implementation calls <code>renderStart()</code> and
099: * <code>renderAttributes()</code>.</p>
100: *
101: * @param context <code>FacesContext</code> for the current request
102: * @param component <code>UIComponent</code> to be decoded
103: *
104: * @exception NullPointerException if <code>context</code> or
105: * <code>component</code> is <code>null</code>
106: *
107: * @exception IOException if an input/output error occurs
108: */
109: public void encodeBegin(FacesContext context, UIComponent component)
110: throws IOException {
111:
112: // Enforce NPE requirements in the Javadocs
113: if ((context == null) || (component == null)) {
114: throw new NullPointerException();
115: }
116:
117: if (log.isTraceEnabled()) {
118: log.trace("encodeBegin(id=" + component.getId()
119: + ", family=" + component.getFamily()
120: + ", rendererType=" + component.getRendererType()
121: + ")");
122: }
123:
124: // Render the element and attributes for this component
125: ResponseWriter writer = context.getResponseWriter();
126: renderStart(context, component, writer);
127: renderAttributes(context, component, writer);
128:
129: }
130:
131: /**
132: * <p>Render the children of the specified <code>UIComponent</code>
133: * to the output stream or writer associated with the response we are
134: * creating.</p>
135: *
136: * <p>The default implementation iterates through the children of
137: * this component and renders them.</p>
138: *
139: * @param context <code>FacesContext</code> for the current request
140: * @param component <code>UIComponent</code> to be decoded
141: *
142: * @exception NullPointerException if <code>context</code> or
143: * <code>component</code> is <code>null</code>
144: *
145: * @exception IOException if an input/output error occurs
146: */
147: public void encodeChildren(FacesContext context,
148: UIComponent component) throws IOException {
149:
150: if (context == null || component == null) {
151: throw new NullPointerException();
152: }
153:
154: if (log.isTraceEnabled()) {
155: log.trace("encodeChildren(id=" + component.getId()
156: + ", family=" + component.getFamily()
157: + ", rendererType=" + component.getRendererType()
158: + ")");
159: }
160: Iterator kids = component.getChildren().iterator();
161: while (kids.hasNext()) {
162: UIComponent kid = (UIComponent) kids.next();
163: kid.encodeBegin(context);
164: if (kid.getRendersChildren()) {
165: kid.encodeChildren(context);
166: }
167: kid.encodeEnd(context);
168: }
169: if (log.isTraceEnabled()) {
170: log.trace("encodeChildren(id=" + component.getId()
171: + ") end");
172: }
173:
174: }
175:
176: /**
177: * <p>Render the ending of the specified <code>UIComponent</code>
178: * to the output stream or writer associated with the response we are
179: * creating.</p>
180: *
181: * <p>The default implementation calls <code>renderEnd()</code>.</p>
182: *
183: * @param context <code>FacesContext</code> for the current request
184: * @param component <code>UIComponent</code> to be decoded
185: *
186: * @exception NullPointerException if <code>context</code> or
187: * <code>component</code> is <code>null</code>
188: *
189: * @exception IOException if an input/output error occurs
190: */
191: public void encodeEnd(FacesContext context, UIComponent component)
192: throws IOException {
193:
194: // Enforce NPE requirements in the Javadocs
195: if ((context == null) || (component == null)) {
196: throw new NullPointerException();
197: }
198:
199: if (log.isTraceEnabled()) {
200: log.trace("encodeEnd(id=" + component.getId() + ", family="
201: + component.getFamily() + ", rendererType="
202: + component.getRendererType() + ")");
203: }
204:
205: // Render the element closing for this component
206: ResponseWriter writer = context.getResponseWriter();
207: renderEnd(context, component, writer);
208:
209: }
210:
211: // --------------------------------------------------------- Package Methods
212:
213: // ------------------------------------------------------- Protected Methods
214:
215: /**
216: * <p>Render nested child components by invoking the encode methods
217: * on those components, but only when the <code>rendered</code>
218: * property is <code>true</code>.</p>
219: */
220: protected void encodeRecursive(FacesContext context,
221: UIComponent component) throws IOException {
222:
223: // suppress rendering if "rendered" property on the component is
224: // false.
225: if (!component.isRendered()) {
226: return;
227: }
228:
229: // Render this component and its children recursively
230: if (log.isTraceEnabled()) {
231: log.trace("encodeRecursive(id=" + component.getId()
232: + ", family=" + component.getFamily()
233: + ", rendererType=" + component.getRendererType()
234: + ") encodeBegin");
235: }
236: component.encodeBegin(context);
237: if (component.getRendersChildren()) {
238: if (log.isTraceEnabled()) {
239: log.trace("encodeRecursive(id=" + component.getId()
240: + ") delegating");
241: }
242: component.encodeChildren(context);
243: } else {
244: if (log.isTraceEnabled()) {
245: log.trace("encodeRecursive(id=" + component.getId()
246: + ") recursing");
247: }
248: Iterator kids = component.getChildren().iterator();
249: while (kids.hasNext()) {
250: UIComponent kid = (UIComponent) kids.next();
251: encodeRecursive(context, kid);
252: }
253: }
254: if (log.isTraceEnabled()) {
255: log.trace("encodeRecursive(id=" + component.getId()
256: + ") encodeEnd");
257: }
258: component.encodeEnd(context);
259:
260: }
261:
262: /**
263: * <p>Return <code>true</code> if the specified component is disabled.</p>
264: *
265: * @param component <code>UIComponent</code> to be checked
266: */
267: protected boolean isDisabled(UIComponent component) {
268:
269: Object disabled = component.getAttributes().get("disabled");
270: if (disabled == null) {
271: return (false);
272: }
273: if (disabled instanceof String) {
274: return (Boolean.valueOf((String) disabled).booleanValue());
275: } else {
276: return (disabled.equals(Boolean.TRUE));
277: }
278:
279: }
280:
281: /**
282: * <p>Return <code>true</code> if the specified component is read only.</p>
283: *
284: * @param component <code>UIComponent</code> to be checked
285: */
286: protected boolean isReadOnly(UIComponent component) {
287:
288: Object readonly = component.getAttributes().get("readonly");
289: if (readonly == null) {
290: return (false);
291: }
292: if (readonly instanceof String) {
293: return (Boolean.valueOf((String) readonly).booleanValue());
294: } else {
295: return (readonly.equals(Boolean.TRUE));
296: }
297:
298: }
299:
300: /**
301: * <p>Render the element attributes for the generated markup related to this
302: * component. Simple renderers that create a single markup element
303: * for this component should override this method and include calls to
304: * to <code>writeAttribute()</code> and <code>writeURIAttribute</code>
305: * on the specified <code>ResponseWriter</code>.</p>
306: *
307: * <p>The default implementation does nothing.</p>
308: *
309: * @param context <code>FacesContext</code> for the current request
310: * @param component <code>EditableValueHolder</code> component whose
311: * submitted value is to be stored
312: * @param writer <code>ResponseWriter</code> to which the element
313: * start should be rendered
314: *
315: * @exception IOException if an input/output error occurs
316: */
317: protected void renderAttributes(FacesContext context,
318: UIComponent component, ResponseWriter writer)
319: throws IOException {
320:
321: }
322:
323: /**
324: * <p>Render the element end for the generated markup related to this
325: * component. Simple renderers that create a single markup element
326: * for this component should override this method and include a call
327: * to <code>endElement()</code> on the specified
328: * <code>ResponseWriter</code>.</p>
329: *
330: * <p>The default implementation does nothing.</p>
331: *
332: * @param context <code>FacesContext</code> for the current request
333: * @param component <code>EditableValueHolder</code> component whose
334: * submitted value is to be stored
335: * @param writer <code>ResponseWriter</code> to which the element
336: * start should be rendered
337: *
338: * @exception IOException if an input/output error occurs
339: */
340: protected void renderEnd(FacesContext context,
341: UIComponent component, ResponseWriter writer)
342: throws IOException {
343:
344: }
345:
346: /**
347: * <p>Render any boolean attributes on the specified list that have
348: * <code>true</code> values on the corresponding attribute of the
349: * specified <code>UIComponent</code>.</p>
350: *
351: * @param context <code>FacesContext</code> for the current request
352: * @param component <code>EditableValueHolder</code> component whose
353: * submitted value is to be stored
354: * @param writer <code>ResponseWriter</code> to which the element
355: * start should be rendered
356: * @param names List of attribute names to be passed through
357: *
358: * @exception IOException if an input/output error occurs
359: */
360: protected void renderBoolean(FacesContext context,
361: UIComponent component, ResponseWriter writer,
362: String names[]) throws IOException {
363:
364: if (names == null) {
365: return;
366: }
367: Map attributes = component.getAttributes();
368: boolean flag;
369: Object value;
370: for (int i = 0; i < names.length; i++) {
371: value = attributes.get(names[i]);
372: if (value != null) {
373: if (value instanceof String) {
374: flag = Boolean.valueOf((String) value)
375: .booleanValue();
376: } else {
377: flag = Boolean.valueOf(value.toString())
378: .booleanValue();
379: }
380: if (flag) {
381: writer.writeAttribute(names[i], names[i], names[i]);
382: flag = false;
383: }
384: }
385: }
386:
387: }
388:
389: /**
390: * <p>Render any attributes on the specified list directly to the
391: * specified <code>ResponseWriter</code> for which the specified
392: * <code>UIComponent</code> has a non-<code>null</code> attribute value.
393: * This method may be used to "pass through" commonly used attribute
394: * name/value pairs with a minimum of code.</p>
395: *
396: * @param context <code>FacesContext</code> for the current request
397: * @param component <code>EditableValueHolder</code> component whose
398: * submitted value is to be stored
399: * @param writer <code>ResponseWriter</code> to which the element
400: * start should be rendered
401: * @param names List of attribute names to be passed through
402: *
403: * @exception IOException if an input/output error occurs
404: */
405: protected void renderPassThrough(FacesContext context,
406: UIComponent component, ResponseWriter writer,
407: String names[]) throws IOException {
408:
409: if (names == null) {
410: return;
411: }
412: Map attributes = component.getAttributes();
413: Object value;
414: for (int i = 0; i < names.length; i++) {
415: value = attributes.get(names[i]);
416: if (value != null) {
417: if (value instanceof String) {
418: writer.writeAttribute(names[i], value, names[i]);
419: } else {
420: writer.writeAttribute(names[i], value.toString(),
421: names[i]);
422: }
423: }
424: }
425:
426: }
427:
428: /**
429: * <p>Render the element start for the generated markup related to this
430: * component. Simple renderers that create a single markup element
431: * for this component should override this method and include a call
432: * to <code>startElement()</code> on the specified
433: * <code>ResponseWriter</code>.</p>
434: *
435: * <p>The default implementation does nothing.</p>
436: *
437: * @param context <code>FacesContext</code> for the current request
438: * @param component <code>EditableValueHolder</code> component whose
439: * submitted value is to be stored
440: * @param writer <code>ResponseWriter</code> to which the element
441: * start should be rendered
442: *
443: * @exception IOException if an input/output error occurs
444: */
445: protected void renderStart(FacesContext context,
446: UIComponent component, ResponseWriter writer)
447: throws IOException {
448:
449: }
450:
451: /**
452: * <p>If a submitted value was included on this request, store it in the
453: * component as appropriate.</p>
454: *
455: * <p>The default implementation determines whether this component
456: * implements <code>EditableValueHolder</code>. If so, it checks for a
457: * request parameter with the same name as the <code>clientId</code>
458: * of this <code>UIComponent</code>. If there is such a parameter, its
459: * value is passed (as a String) to the <code>setSubmittedValue()</code>
460: * method on the <code>EditableValueHolder</code> component.</p>
461: *
462: * @param context <code>FacesContext</code> for the current request
463: * @param component <code>EditableValueHolder</code> component whose
464: * submitted value is to be stored
465: */
466: protected void setSubmittedValue(FacesContext context,
467: UIComponent component) {
468:
469: if (!(component instanceof EditableValueHolder)) {
470: return;
471: }
472: String clientId = component.getClientId(context);
473: Map parameters = context.getExternalContext()
474: .getRequestParameterMap();
475: if (parameters.containsKey(clientId)) {
476: if (log.isTraceEnabled()) {
477: log.trace("setSubmittedValue(" + clientId + ","
478: + (String) parameters.get(clientId));
479: }
480: component.getAttributes().put("submittedValue",
481: parameters.get(clientId));
482: }
483:
484: }
485:
486: // --------------------------------------------------------- Private Methods
487:
488: /**
489: * <p>Decode the current state of the specified UIComponent from the
490: * request contained in the specified <code>FacesContext</code>, and
491: * attempt to convert this state information into an object of the
492: * type equired for this component.</p>
493: *
494: * @param context FacesContext for the request we are processing
495: * @param component UIComponent to be decoded
496: *
497: * @exception NullPointerException if context or component is null
498: */
499: /*
500: public void decode(FacesContext context, UIComponent component) {
501:
502: // Enforce NPE requirements in the Javadocs
503: if ((context == null) || (component == null)) {
504: throw new NullPointerException();
505: }
506:
507: // Only input components need to be decoded
508: if (!(component instanceof UIInput)) {
509: return;
510: }
511: UIInput input = (UIInput) component;
512:
513: // Save the old value for use in generating ValueChangedEvents
514: Object oldValue = input.getValue();
515: if (oldValue instanceof String) {
516: try {
517: oldValue = getAsObject(context, component, (String) oldValue);
518: } catch (ConverterException e) {
519: ;
520: }
521: }
522: input.setPrevious(oldValue);
523:
524: // Decode and convert (if needed) the new value
525: String clientId = component.getClientId(context);
526: Map map = context.getExternalContext().getRequestParameterMap();
527: String newString = (String) map.get(clientId);
528: Object newValue = null;
529: try {
530: newValue = getAsObject(context, component, newString);
531: input.setValue(newValue);
532: input.setValid(true);
533: } catch (ConverterException e) {
534: input.setValue(newValue);
535: input.setValid(false);
536: addConverterMessage(context, component, e.getMessage());
537: }
538:
539: }
540: */
541:
542: // --------------------------------------------------------- Package Methods
543:
544: // ------------------------------------------------------- Protected Methods
545:
546: /**
547: * <p>Add an error message denoting a conversion failure.</p>
548: *
549: * @param context The <code>FacesContext</code> for this request
550: * @param component The <code>UIComponent</code> that experienced
551: * the conversion failure
552: * @param text The text of the error message
553: */
554: /*
555: protected void addConverterMessage(FacesContext context,
556: UIComponent component,
557: String text) {
558:
559: String clientId = component.getClientId(context);
560: FacesMessage message = new FacesMessage
561: (text,
562: "Conversion error on component '" + clientId + "'");
563: context.addMessage(clientId, message);
564:
565: }
566: */
567:
568: /**
569: * <p>Convert the String representation of this component's value
570: * to the corresponding Object representation. The default
571: * implementation utilizes the <code>getAsObject()</code> method of any
572: * associated <code>Converter</code>.</p>
573: *
574: * @param context The <code>FacesContext</code> for this request
575: * @param component The <code>UIComponent</code> whose value is
576: * being converted
577: * @param value The String representation to be converted
578: *
579: * @exception ConverterException if conversion fails
580: */
581: /*
582: protected Object getAsObject(FacesContext context, UIComponent component,
583: String value) throws ConverterException {
584:
585: // Identify any Converter associated with this component value
586: ValueBinding vb = component.getValueBinding("value");
587: Converter converter = null;
588: if (component instanceof ValueHolder) {
589: // Acquire explicitly assigned Converter (if any)
590: converter = ((ValueHolder) component).getConverter();
591: }
592: if ((converter == null) && (vb != null)) {
593: Class type = vb.getType(context);
594: if ((type == null) || (type == String.class)) {
595: return (value); // No conversion required for Strings
596: }
597: // Acquire implicit by-type Converter (if any)
598: converter = context.getApplication().createConverter(type);
599: }
600:
601: // Convert the result if we identified a Converter
602: if (converter != null) {
603: return (converter.getAsObject(context, component, value));
604: } else {
605: return (value);
606: }
607:
608: }
609: */
610:
611: /**
612: * <p>Convert the Object representation of this component's value
613: * to the corresponding String representation. The default implementation
614: * utilizes the <code>getAsString()</code> method of any associated
615: * <code>Converter</code>.</p>
616: *
617: * @param context The <code>FacesContext</code> for this request
618: * @param component The <code>UIComponent</code> whose value is
619: * being converted
620: * @param value The Object representation to be converted
621: *
622: * @exception ConverterException if conversion fails
623: */
624: protected String getAsString(FacesContext context,
625: UIComponent component, Object value)
626: throws ConverterException {
627:
628: // Identify any Converter associated with this component value
629: ValueBinding vb = component.getValueBinding("value");
630: Converter converter = null;
631: if (component instanceof ValueHolder) {
632: // Acquire explicitly assigned Converter (if any)
633: converter = ((ValueHolder) component).getConverter();
634: }
635: if ((converter == null) && (vb != null)) {
636: // Acquire implicit by-type Converter (if any)
637: Class type = vb.getType(context);
638: if (type != null) {
639: converter = context.getApplication().createConverter(
640: type);
641: }
642: }
643:
644: // Convert the result if we identified a Converter
645: if (converter != null) {
646: return (converter.getAsString(context, component, value));
647: } else if (value == null) {
648: return ("");
649: } else if (value instanceof String) {
650: return ((String) value);
651: } else {
652: return (value.toString());
653: }
654:
655: }
656:
657: }
|