001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.rave.web.ui.renderer;
042:
043: import com.sun.rave.web.ui.component.Page;
044: import com.sun.rave.web.ui.component.util.Util;
045: import com.sun.rave.web.ui.model.Markup;
046: import com.sun.rave.web.ui.theme.Theme;
047: import com.sun.rave.web.ui.theme.ThemeStyles;
048: import com.sun.rave.web.ui.util.LogUtil;
049: import com.sun.rave.web.ui.util.RenderingUtilities;
050: import com.sun.rave.web.ui.util.ThemeUtilities;
051:
052: import java.io.IOException;
053: import java.util.Iterator;
054: import java.util.Map;
055: import javax.faces.application.Application;
056: import javax.faces.component.EditableValueHolder;
057: import javax.faces.component.UIComponent;
058: import javax.faces.component.ValueHolder;
059: import javax.faces.component.UIViewRoot;
060: import javax.faces.context.ExternalContext;
061: import javax.faces.context.FacesContext;
062: import javax.faces.context.ResponseWriter;
063: import javax.faces.convert.Converter;
064: import javax.faces.el.ValueBinding;
065: import javax.faces.render.Renderer;
066:
067: /**
068: * <p>Abstract base class for concrete implementations of
069: * <code>javax.faces.render.Renderer</code> for JavaServer Faces
070: * component libraries.</p>
071: */
072:
073: public abstract class AbstractRenderer extends Renderer {
074:
075: // ------------------------------------------------------ Manifest Constants
076:
077: /**
078: * <p>Base naem of the resource bundle we will use for localization.</p>
079: */
080: protected static final String BUNDLE = "com.sun.rave.web.ui.renderer.Bundle"; // NOI18N
081:
082: /**
083: * <p>The list of attribute names in the HTML 4.01 Specification that
084: * correspond to the entity type <em>%events;</em>.</p>
085: */
086: public static final String EVENTS_ATTRIBUTES[] = { "onClick",
087: "onDblClick",
088: "onChange", // NOI18N
089: "onMouseDown", "onMouseUp", "onMouseOver", "onMouseMove",
090: "onMouseOut", // NOI18N
091: "onKeyPress", "onKeyDown", "onKeyUp", // NOI18N
092: };
093:
094: /**
095: * <p>The list of attribute names in the HTML 4.01 Specification that
096: * correspond to the entity type <em>%i18n;</em>.</p>
097: */
098: public static final String I18N_ATTRIBUTES[] = { "dir", "lang", }; // NOI18N
099:
100: // -------------------------------------------------------- Static Variables
101:
102: // ---------------------------------------------------------- Public Methods
103:
104: /**
105: * <p>Decode any new state of the specified <code>UIComponent</code>
106: * from the request contained in the specified <code>FacesContext</code>,
107: * and store that state on the <code>UIComponent</code>.</p>
108: *
109: * <p>The default implementation calls <code>setSubmittedValue()</code>
110: * on components that implement EditableValueHolder (i.e. input fields)</p>
111: *
112: * @param context <code>FacesContext</code> for the current request
113: * @param component <code>UIComponent</code> to be decoded
114: *
115: * @exception NullPointerException if <code>context</code> or
116: * <code>component</code> is <code>null</code>
117: */
118: public void decode(FacesContext context, UIComponent component) {
119:
120: // Enforce NPE requirements in the Javadocs
121: if ((context == null) || (component == null)) {
122: throw new NullPointerException();
123: }
124:
125: // Save submitted value on EditableValueHolder components
126: // unless they are disabled or read only
127: if (component instanceof EditableValueHolder) {
128: setSubmittedValue(context, component);
129: }
130:
131: }
132:
133: /**
134: * <p>Render the beginning 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 calls <code>renderStart()</code> and
139: * <code>renderAttributes()</code>.</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 encodeBegin(FacesContext context, UIComponent component)
150: throws IOException {
151:
152: // Enforce NPE requirements in the Javadocs
153: if ((context == null) || (component == null)) {
154: throw new NullPointerException();
155: }
156:
157: /*
158: if (LogUtil.finestEnabled()) {
159: LogUtil.finest("encodeBegin(id=" + component.getId() +
160: ", family=" + component.getFamily() +
161: ", rendererType=" + component.getRendererType() + ")");
162: }
163: */
164:
165: // Render the element and attributes for this component
166: if (component.isRendered()) {
167: ResponseWriter writer = context.getResponseWriter();
168: renderStart(context, component, writer);
169: renderAttributes(context, component, writer);
170: }
171:
172: }
173:
174: /**
175: * <p>Render the children of the specified <code>UIComponent</code>
176: * to the output stream or writer associated with the response we are
177: * creating.</p>
178: *
179: * <p>The default implementation iterates through the children of
180: * this component and renders them.</p>
181: *
182: * @param context <code>FacesContext</code> for the current request
183: * @param component <code>UIComponent</code> to be decoded
184: *
185: * @exception NullPointerException if <code>context</code> or
186: * <code>component</code> is <code>null</code>
187: *
188: * @exception IOException if an input/output error occurs
189: */
190: // We shouldn't bother with a default implementation - this is exactly
191: // what happens when you rendersChildren = false. Why duplicate the
192: // code here?
193: public void encodeChildren(FacesContext context,
194: UIComponent component) throws IOException {
195:
196: // Enforce NPE requirements in the Javadocs
197: if (context == null || component == null) {
198: throw new NullPointerException();
199: }
200:
201: /*
202: if (LogUtil.finestEnabled()) {
203: LogUtil.finest("encodeChildren(id=" + component.getId() +
204: ", family=" + component.getFamily() +
205: ", rendererType=" + component.getRendererType() + ")");
206: }
207: */
208:
209: if (component.isRendered()) {
210: Iterator kids = component.getChildren().iterator();
211: while (kids.hasNext()) {
212: UIComponent kid = (UIComponent) kids.next();
213: RenderingUtilities.renderComponent(kid, context);
214: }
215: }
216:
217: }
218:
219: /**
220: * <p>Render the ending of the specified <code>UIComponent</code>
221: * to the output stream or writer associated with the response we are
222: * creating.</p>
223: *
224: * <p>The default implementation calls <code>renderEnd()</code>.</p>
225: *
226: * @param context <code>FacesContext</code> for the current request
227: * @param component <code>UIComponent</code> to be decoded
228: *
229: * @exception NullPointerException if <code>context</code> or
230: * <code>component</code> is <code>null</code>
231: *
232: * @exception IOException if an input/output error occurs
233: */
234: public void encodeEnd(FacesContext context, UIComponent component)
235: throws IOException {
236:
237: // Enforce NPE requirements in the Javadocs
238: if ((context == null) || (component == null)) {
239: throw new NullPointerException();
240: }
241:
242: /*
243: if (LogUtil.finestEnabled()) {
244: LogUtil.finest("encodeEnd(id=" + component.getId() +
245: ", family=" + component.getFamily() +
246: ", rendererType=" + component.getRendererType() + ")");
247: }
248: */
249:
250: // Render the element closing for this component
251: if (component.isRendered()) {
252: ResponseWriter writer = context.getResponseWriter();
253: renderEnd(context, component, writer);
254: }
255:
256: }
257:
258: // --------------------------------------------------------- Package Methods
259:
260: // ------------------------------------------------------- Protected Methods
261:
262: /**
263: * <p>Render any boolean attributes on the specified list that have
264: * <code>true</code> values on the corresponding attribute of the
265: * specified <code>UIComponent</code>. Attribute names are
266: * converted to lower case in the rendered output.</p>
267: *
268: * @param context <code>FacesContext</code> for the current request
269: * @param component <code>EditableValueHolder</code> component whose
270: * submitted value is to be stored
271: * @param writer <code>ResponseWriter</code> to which the element
272: * start should be rendered
273: * @param names List of attribute names to be passed through
274: *
275: * @exception IOException if an input/output error occurs
276: */
277: protected void addBooleanAttributes(FacesContext context,
278: UIComponent component, ResponseWriter writer,
279: String names[]) throws IOException {
280:
281: if (names == null) {
282: return;
283: }
284: Map attributes = component.getAttributes();
285: boolean flag;
286: Object value;
287: for (int i = 0; i < names.length; i++) {
288: value = attributes.get(names[i]);
289: if (value != null) {
290: if (value instanceof String) {
291: flag = Boolean.valueOf((String) value)
292: .booleanValue();
293: } else {
294: flag = Boolean.valueOf(value.toString())
295: .booleanValue();
296: }
297: if (flag) {
298: writer.writeAttribute(names[i].toLowerCase(),
299: names[i].toLowerCase(), names[i]);
300: flag = false;
301: }
302: }
303: }
304:
305: }
306:
307: // Core attributes that are simple pass throughs
308: private static final String coreAttributes[] = { "style", "title" };
309:
310: /**
311: * <p>Render the "core" set of attributes for this <code>UIComponent</code>.
312: * The default implementation conditionally generates the following
313: * attributes with values as specified.</p>
314: * <ul>
315: * <li><strong>id</strong> - If this component has a non-<code>null</code>
316: * <code>id</code> property, and the identifier does not start with
317: * <code>UIViewRoot.UNIQUE_ID_PREFIX</code>, render the
318: * <code>clientId</code>.</li>
319: * <li><strong>class</strong> - If this component has a
320: * non-<code>null</code> <code>styleClass</code> attribute, render its
321: * value, combined with the syles parameter (if any).</li>
322: * <li><strong>style</strong> - If this component has a
323: * non-<code>null</code> <code>style</code> attribute, render its
324: * value.</li>
325: * <li><strong>title</strong> - If this component has a
326: * non-<code>null</code> <code>title</code> attribute, render its
327: * value.</li>
328: * </ul>
329: *
330: * @param context <code>FacesContext</code> for the current request
331: * @param component <code>EditableValueHolder</code> component whose
332: * submitted value is to be stored
333: * @param writer <code>ResponseWriter</code> to which the element
334: * start should be rendered
335: * @param styles Space-separated list of CSS style classes to add
336: * to the <code>class</code> attribute, or <code>null</code> for none
337: *
338: * @exception IOException if an input/output error occurs
339: */
340: protected void addCoreAttributes(FacesContext context,
341: UIComponent component, ResponseWriter writer, String styles)
342: throws IOException {
343:
344: String id = component.getId();
345:
346: writer.writeAttribute("id", component.getClientId(context),
347: "id");
348:
349: RenderingUtilities.renderStyleClass(context, writer, component,
350: styles);
351: addStringAttributes(context, component, writer, coreAttributes);
352:
353: }
354:
355: /**
356: * <p>Render any Integer attributes on the specified list that do not have
357: * Integer.MIN_VALUE values on the corresponding attribute of the
358: * specified <code>UIComponent</code>. Attribute names are converted to
359: * lower case in the rendered output.</p>
360: *
361: * @param context <code>FacesContext</code> for the current request
362: * @param component <code>EditableValueHolder</code> component whose
363: * submitted value is to be stored
364: * @param writer <code>ResponseWriter</code> to which the element
365: * start should be rendered
366: * @param names List of attribute names to be passed through
367: *
368: * @exception IOException if an input/output error occurs
369: */
370: protected void addIntegerAttributes(FacesContext context,
371: UIComponent component, ResponseWriter writer,
372: String names[]) throws IOException {
373:
374: if (names == null) {
375: return;
376: }
377: Map attributes = component.getAttributes();
378: boolean flag;
379: Object value;
380: for (int i = 0; i < names.length; i++) {
381: value = attributes.get(names[i]);
382: if ((value != null) && (value instanceof Integer)) {
383: Integer ivalue = (Integer) value;
384: if (!(ivalue.intValue() == Integer.MIN_VALUE)) {
385: writer.writeAttribute(names[i].toLowerCase(),
386: ivalue, names[i]);
387: }
388: }
389: }
390:
391: }
392:
393: /**
394: * <p>Add any attributes on the specified list directly to the
395: * specified <code>ResponseWriter</code> for which the specified
396: * <code>UIComponent</code> has a non-<code>null</code> String value.
397: * This method may be used to "pass through" commonly used attribute
398: * name/value pairs with a minimum of code. Attribute names are
399: * converted to lower case in the rendered output.</p>
400: *
401: * @param context <code>FacesContext</code> for the current request
402: * @param component <code>EditableValueHolder</code> component whose
403: * submitted value is to be stored
404: * @param writer <code>ResponseWriter</code> to which the element
405: * start should be rendered
406: * @param names List of attribute names to be passed through
407: *
408: * @exception IOException if an input/output error occurs
409: */
410: protected static void addStringAttributes(FacesContext context,
411: UIComponent component, ResponseWriter writer,
412: String names[]) throws IOException {
413:
414: if (names == null) {
415: return;
416: }
417: Map attributes = component.getAttributes();
418: Object value;
419: for (int i = 0; i < names.length; i++) {
420: value = attributes.get(names[i]);
421: if (value != null) {
422: if (value instanceof String) {
423: writer.writeAttribute(names[i].toLowerCase(),
424: (String) value, names[i]);
425: } else {
426: writer.writeAttribute(names[i].toLowerCase(), value
427: .toString(), names[i]);
428: }
429: }
430: }
431:
432: }
433:
434: /**
435: * <p>Return the <code>Application</code> instance for this
436: * web application.</p>
437: */
438: protected Application getApplication() {
439:
440: return getFacesContext().getApplication();
441:
442: }
443:
444: /**
445: * <p>Return the value to be stored, as an Object that has been
446: * converted from the String representation (if necessary), or
447: * <code>null</code> if the String representation is null.</p>
448: *
449: * @param context FacesContext for the current request
450: * @param component Component whose value is being processed
451: * (must be a component that implements ValueHolder
452: * @param value String representation of the value
453: */
454: protected Object getAsObject(FacesContext context,
455: UIComponent component, String value) {
456:
457: if (value == null) {
458: return null;
459: }
460: Converter converter = ((ValueHolder) component).getConverter();
461: if (converter == null) {
462: ValueBinding vb = component.getValueBinding("value");
463: if (vb != null) {
464: Class clazz = vb.getType(context);
465: if (clazz != null) {
466: converter = getApplication().createConverter(clazz);
467: }
468: }
469: }
470: if (converter != null) {
471: return converter.getAsObject(context, component, value);
472: } else {
473: return value;
474: }
475:
476: }
477:
478: /**
479: * <p>Return the value to be rendered, as a String (converted
480: * if necessary), or <code>null</code> if the value is null.</p>
481: *
482: * @param context FacesContext for the current request
483: * @param component Component whose value is to be retrieved (must be
484: * a component that implements ValueHolder)
485: */
486: protected String getAsString(FacesContext context,
487: UIComponent component) {
488: if (component instanceof EditableValueHolder) {
489: Object submittedValue = ((EditableValueHolder) component)
490: .getSubmittedValue();
491: if (submittedValue != null) {
492: return (String) submittedValue;
493: }
494: }
495: Object value = ((ValueHolder) component).getValue();
496: if (value == null) {
497: return null;
498: }
499: Converter converter = ((ValueHolder) component).getConverter();
500: if (converter == null) {
501: if (value instanceof String) {
502: return (String) value;
503: }
504: converter = getApplication().createConverter(
505: value.getClass());
506: }
507: if (converter != null) {
508: return converter.getAsString(context, component, value);
509: } else {
510: return value.toString();
511: }
512: }
513:
514: /**
515: * <p>Return the <code>ExternalContext</code> instance for the current
516: * request.</p>
517: */
518: protected ExternalContext getExternalContext() {
519:
520: return (FacesContext.getCurrentInstance().getExternalContext());
521:
522: }
523:
524: /**
525: * <p>Return the <code>FacesContext</code> instance for the current
526: * request.</p>
527: */
528: protected FacesContext getFacesContext() {
529:
530: return (FacesContext.getCurrentInstance());
531:
532: }
533:
534: /**
535: * <p>Retrieve the submitted value from the request parameters for
536: * this request. The default implementation retrieves the parameter
537: * value that corresponds to the client identifier of this component.</p>
538: *
539: * @param context <code>FacesContext</code> for the current request
540: * @param component <code>UIComponent</code> whose
541: * submitted value is to be retrieved
542: */
543: protected Object getSubmittedValue(FacesContext context,
544: UIComponent component) {
545:
546: String clientId = component.getClientId(context);
547: Map parameters = context.getExternalContext()
548: .getRequestParameterMap();
549: return parameters.get(clientId);
550:
551: }
552:
553: /**
554: * <p>Return <code>true</code> if the specified component is disabled.</p>
555: *
556: * @param component <code>UIComponent</code> to be checked
557: */
558: protected boolean isDisabled(UIComponent component) {
559:
560: Object disabled = component.getAttributes().get("disabled");
561: if (disabled == null) {
562: return (false);
563: }
564: if (disabled instanceof String) {
565: return (Boolean.valueOf((String) disabled).booleanValue());
566: } else {
567: return (disabled.equals(Boolean.TRUE));
568: }
569:
570: }
571:
572: /**
573: * <p>Return <code>true</code> if we are we running in a portlet
574: * environment, as opposed to a servlet based web application.</p>
575: *
576: * @param context <code>FacesContext</code> for the current request
577: */
578: protected boolean isPortlet(FacesContext context) {
579:
580: return false; // TODO - implement a dynamic check
581:
582: }
583:
584: /**
585: * <p>Return <code>true</code> if the specified component is read only.</p>
586: *
587: * @param component <code>UIComponent</code> to be checked
588: */
589: protected boolean isReadOnly(UIComponent component) {
590:
591: Object readonly = component.getAttributes().get("readonly");
592: if (readonly == null) {
593: return (false);
594: }
595: if (readonly instanceof String) {
596: return (Boolean.valueOf((String) readonly).booleanValue());
597: } else {
598: return (readonly.equals(Boolean.TRUE));
599: }
600:
601: }
602:
603: /**
604: * <p>Render the element attributes for the generated markup related to this
605: * component. Simple renderers that create a single markup element
606: * for this component should override this method and include calls to
607: * to <code>writeAttribute()</code> and <code>writeURIAttribute</code>
608: * on the specified <code>ResponseWriter</code>.</p>
609: *
610: * <p>The default implementation does nothing.</p>
611: *
612: * @param context <code>FacesContext</code> for the current request
613: * @param component <code>EditableValueHolder</code> component whose
614: * submitted value is to be stored
615: * @param writer <code>ResponseWriter</code> to which the element
616: * start should be rendered
617: *
618: * @exception IOException if an input/output error occurs
619: */
620: protected void renderAttributes(FacesContext context,
621: UIComponent component, ResponseWriter writer)
622: throws IOException {
623:
624: }
625:
626: /**
627: * <p>Render the element end for the generated markup related to this
628: * component. Simple renderers that create a single markup element
629: * for this component should override this method and include a call
630: * to <code>endElement()</code> on the specified
631: * <code>ResponseWriter</code>.</p>
632: *
633: * <p>The default implementation does nothing.</p>
634: *
635: * @param context <code>FacesContext</code> for the current request
636: * @param component <code>EditableValueHolder</code> component whose
637: * submitted value is to be stored
638: * @param writer <code>ResponseWriter</code> to which the element
639: * start should be rendered
640: *
641: * @exception IOException if an input/output error occurs
642: */
643: protected void renderEnd(FacesContext context,
644: UIComponent component, ResponseWriter writer)
645: throws IOException {
646:
647: }
648:
649: /**
650: * <p>Render the specified markup to the current response.</p>
651: *
652: * @param context <code>FacesContext</code> for the current request
653: * @param component <code>UIComponent</code> associated with this markup
654: * @param writer <code>ResponseWriter</code> to which the markup
655: * should be rendered
656: * @param markup {@link Markup} to be rendered
657: */
658: protected void renderMarkup(FacesContext context,
659: UIComponent component, ResponseWriter writer, Markup markup)
660: throws IOException {
661:
662: writer.write(markup.getMarkup());
663:
664: }
665:
666: /**
667: * <p>Render the element start for the generated markup related to this
668: * component. Simple renderers that create a single markup element
669: * for this component should override this method and include a call
670: * to <code>startElement()</code> on the specified
671: * <code>ResponseWriter</code>.</p>
672: *
673: * <p>The default implementation does nothing.</p>
674: *
675: * @param context <code>FacesContext</code> for the current request
676: * @param component <code>EditableValueHolder</code> component whose
677: * submitted value is to be stored
678: * @param writer <code>ResponseWriter</code> to which the element
679: * start should be rendered
680: *
681: * @exception IOException if an input/output error occurs
682: */
683: protected void renderStart(FacesContext context,
684: UIComponent component, ResponseWriter writer)
685: throws IOException {
686:
687: }
688:
689: /**
690: * <p>If a submitted value was included on this request, store it in the
691: * component as appropriate.</p>
692: *
693: * <p>The default implementation determines whether this component
694: * implements <code>EditableValueHolder</code>. If so, it checks for a
695: * request parameter with the same name as the <code>clientId</code>
696: * of this <code>UIComponent</code>. If there is such a parameter, its
697: * value is passed (as a String) to the <code>setSubmittedValue()</code>
698: * method on the <code>EditableValueHolder</code> component.</p>
699: *
700: * @param context <code>FacesContext</code> for the current request
701: * @param component <code>EditableValueHolder</code> component whose
702: * submitted value is to be stored
703: */
704: protected void setSubmittedValue(FacesContext context,
705: UIComponent component) {
706:
707: if (!(component instanceof EditableValueHolder)) {
708: return;
709: }
710: component.getAttributes().put("submittedValue", // NOI18N
711: getSubmittedValue(context, component));
712:
713: }
714:
715: }
|