com.sun.webui.jsf.renderkit.html.AbstractRenderer Maven / Gradle / Ivy
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://woodstock.dev.java.net/public/CDDLv1.0.html.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at https://woodstock.dev.java.net/public/CDDLv1.0.html.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
*/
/*
* $Id: AbstractRenderer.java,v 1.1.12.1 2009-12-29 04:52:46 jyeary Exp $
*/
package com.sun.webui.jsf.renderkit.html;
import com.sun.webui.jsf.model.Markup;
import com.sun.webui.jsf.util.RenderingUtilities;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import javax.faces.application.Application;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.el.ValueExpression;
import javax.faces.render.Renderer;
/**
* Abstract base class for concrete implementations of
* javax.faces.render.Renderer
for JavaServer Faces
* component libraries.
*/
public abstract class AbstractRenderer extends Renderer {
// ------------------------------------------------------ Manifest Constants
/**
* Base naem of the resource bundle we will use for localization.
*/
protected static final String BUNDLE =
"com.sun.webui.jsf.renderkit.html.Bundle"; // NOI18N
/**
* The list of attribute names in the HTML 4.01 Specification that
* correspond to the entity type %events;.
*/
public static final String EVENTS_ATTRIBUTES[] = {"onClick", "onDblClick", "onChange", // NOI18N
"onMouseDown", "onMouseUp", "onMouseOver", "onMouseMove", "onMouseOut", // NOI18N
"onKeyPress", "onKeyDown", "onKeyUp", // NOI18N
};
/**
* The list of attribute names in the HTML 4.01 Specification that
* correspond to the entity type %i18n;.
*/
public static final String I18N_ATTRIBUTES[] = {"dir", "lang",}; // NOI18N
// -------------------------------------------------------- Static Variables
// ---------------------------------------------------------- Public Methods
/**
* Decode any new state of the specified UIComponent
* from the request contained in the specified FacesContext
,
* and store that state on the UIComponent
.
*
* The default implementation calls setSubmittedValue()
* on components that implement EditableValueHolder (i.e. input fields)
*
* @param context FacesContext
for the current request
* @param component UIComponent
to be decoded
*
* @exception NullPointerException if context
or
* component
is null
*/
@Override
public void decode(FacesContext context, UIComponent component) {
// Enforce NPE requirements in the Javadocs
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
// Save submitted value on EditableValueHolder components
// unless they are disabled or read only
if (component instanceof EditableValueHolder) {
setSubmittedValue(context, component);
}
}
/**
* Render the beginning of the specified UIComponent
* to the output stream or writer associated with the response we are
* creating.
*
* The default implementation calls renderStart()
and
* renderAttributes()
.
*
* @param context FacesContext
for the current request
* @param component UIComponent
to be decoded
*
* @exception NullPointerException if context
or
* component
is null
*
* @exception IOException if an input/output error occurs
*/
@Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
// Enforce NPE requirements in the Javadocs
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
/*
if (LogUtil.finestEnabled()) {
LogUtil.finest("encodeBegin(id=" + component.getId() +
", family=" + component.getFamily() +
", rendererType=" + component.getRendererType() + ")");
}
*/
// Render the element and attributes for this component
if (component.isRendered()) {
ResponseWriter writer = context.getResponseWriter();
renderStart(context, component, writer);
renderAttributes(context, component, writer);
}
}
/**
* Render the children of the specified UIComponent
* to the output stream or writer associated with the response we are
* creating.
*
* The default implementation iterates through the children of
* this component and renders them.
*
* @param context FacesContext
for the current request
* @param component UIComponent
to be decoded
*
* @exception NullPointerException if context
or
* component
is null
*
* @exception IOException if an input/output error occurs
*/
// We shouldn't bother with a default implementation - this is exactly
// what happens when you rendersChildren = false. Why duplicate the
// code here?
@Override
public void encodeChildren(FacesContext context, UIComponent component)
throws IOException {
// Enforce NPE requirements in the Javadocs
if (context == null || component == null) {
throw new NullPointerException();
}
/*
if (LogUtil.finestEnabled()) {
LogUtil.finest("encodeChildren(id=" + component.getId() +
", family=" + component.getFamily() +
", rendererType=" + component.getRendererType() + ")");
}
*/
if (component.isRendered()) {
Iterator kids = component.getChildren().iterator();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
RenderingUtilities.renderComponent(kid, context);
}
}
}
/**
* Render the ending of the specified UIComponent
* to the output stream or writer associated with the response we are
* creating.
*
* The default implementation calls renderEnd()
.
*
* @param context FacesContext
for the current request
* @param component UIComponent
to be decoded
*
* @exception NullPointerException if context
or
* component
is null
*
* @exception IOException if an input/output error occurs
*/
@Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
// Enforce NPE requirements in the Javadocs
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
/*
if (LogUtil.finestEnabled()) {
LogUtil.finest("encodeEnd(id=" + component.getId() +
", family=" + component.getFamily() +
", rendererType=" + component.getRendererType() + ")");
}
*/
// Render the element closing for this component
if (component.isRendered()) {
ResponseWriter writer = context.getResponseWriter();
renderEnd(context, component, writer);
}
}
// --------------------------------------------------------- Package Methods
// ------------------------------------------------------- Protected Methods
/**
* Render any boolean attributes on the specified list that have
* true
values on the corresponding attribute of the
* specified UIComponent
. Attribute names are
* converted to lower case in the rendered output.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
* @param names List of attribute names to be passed through
*
* @exception IOException if an input/output error occurs
*/
protected void addBooleanAttributes(FacesContext context,
UIComponent component,
ResponseWriter writer,
String names[]) throws IOException {
if (names == null) {
return;
}
Map attributes = component.getAttributes();
boolean flag;
Object value;
for (int i = 0; i < names.length; i++) {
value = attributes.get(names[i]);
if (value != null) {
if (value instanceof String) {
flag = Boolean.valueOf((String) value).booleanValue();
} else {
flag = Boolean.valueOf(value.toString()).booleanValue();
}
if (flag) {
writer.writeAttribute(names[i].toLowerCase(),
names[i].toLowerCase(), names[i]);
flag = false;
}
}
}
}
// Core attributes that are simple pass throughs
private static final String coreAttributes[] = {"style", "title"};
/**
* Render the "core" set of attributes for this UIComponent
.
* The default implementation conditionally generates the following
* attributes with values as specified.
*
* - id - If this component has a non-
null
* id
property, and the identifier does not start with
* UIViewRoot.UNIQUE_ID_PREFIX
, render the
* clientId
.
* - class - If this component has a
* non-
null
styleClass
attribute, render its
* value, combined with the syles parameter (if any).
* - style - If this component has a
* non-
null
style
attribute, render its
* value.
* - title - If this component has a
* non-
null
title
attribute, render its
* value.
*
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
* @param styles Space-separated list of CSS style classes to add
* to the class
attribute, or null
for none
*
* @exception IOException if an input/output error occurs
*/
protected void addCoreAttributes(FacesContext context,
UIComponent component,
ResponseWriter writer,
String styles) throws IOException {
String id = component.getId();
writer.writeAttribute("id", component.getClientId(context), "id");
RenderingUtilities.renderStyleClass(context, writer, component, styles);
addStringAttributes(context, component, writer, coreAttributes);
}
/**
* Render any Integer attributes on the specified list that do not have
* Integer.MIN_VALUE values on the corresponding attribute of the
* specified UIComponent
. Attribute names are converted to
* lower case in the rendered output.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
* @param names List of attribute names to be passed through
*
* @exception IOException if an input/output error occurs
*/
protected void addIntegerAttributes(FacesContext context,
UIComponent component,
ResponseWriter writer,
String names[]) throws IOException {
if (names == null) {
return;
}
Map attributes = component.getAttributes();
boolean flag;
Object value;
for (int i = 0; i < names.length; i++) {
value = attributes.get(names[i]);
if ((value != null) && (value instanceof Integer)) {
Integer ivalue = (Integer) value;
if (!(ivalue.intValue() == Integer.MIN_VALUE)) {
writer.writeAttribute(names[i].toLowerCase(), ivalue, names[i]);
}
}
}
}
/**
* Add any attributes on the specified list directly to the
* specified ResponseWriter
for which the specified
* UIComponent
has a non-null
String value.
* This method may be used to "pass through" commonly used attribute
* name/value pairs with a minimum of code. Attribute names are
* converted to lower case in the rendered output.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
* @param names List of attribute names to be passed through
*
* @exception IOException if an input/output error occurs
*/
protected static void addStringAttributes(FacesContext context,
UIComponent component,
ResponseWriter writer,
String names[]) throws IOException {
if (names == null) {
return;
}
Map attributes = component.getAttributes();
Object value;
for (int i = 0; i < names.length; i++) {
value = attributes.get(names[i]);
if (value != null) {
if (value instanceof String) {
writer.writeAttribute(names[i].toLowerCase(),
(String) value, names[i]);
} else {
writer.writeAttribute(names[i].toLowerCase(),
value.toString(), names[i]);
}
}
}
}
/**
* Return the Application
instance for this
* web application.
*/
protected Application getApplication() {
return getFacesContext().getApplication();
}
/**
* Return the value to be stored, as an Object that has been
* converted from the String representation (if necessary), or
* null
if the String representation is null.
*
* @param context FacesContext for the current request
* @param component Component whose value is being processed
* (must be a component that implements ValueHolder
* @param value String representation of the value
*/
protected Object getAsObject(FacesContext context, UIComponent component,
String value) {
if (value == null) {
return null;
}
Converter converter = ((ValueHolder) component).getConverter();
if (converter == null) {
ValueExpression vb = component.getValueExpression("value");
if (vb != null) {
Class clazz = vb.getType(context.getELContext());
if (clazz != null) {
converter =
getApplication().createConverter(clazz);
}
}
}
if (converter != null) {
return converter.getAsObject(context, component, value);
} else {
return value;
}
}
/**
* Return the value to be rendered, as a String (converted
* if necessary), or null
if the value is null.
*
* @param context FacesContext for the current request
* @param component Component whose value is to be retrieved (must be
* a component that implements ValueHolder)
*/
protected String getAsString(FacesContext context, UIComponent component) {
if (component instanceof EditableValueHolder) {
Object submittedValue = ((EditableValueHolder) component).getSubmittedValue();
if (submittedValue != null) {
return (String) submittedValue;
}
}
Object value = ((ValueHolder) component).getValue();
if (value == null) {
return null;
}
Converter converter = ((ValueHolder) component).getConverter();
if (converter == null) {
if (value instanceof String) {
return (String) value;
}
converter = getApplication().createConverter(value.getClass());
}
if (converter != null) {
return converter.getAsString(context, component, value);
} else {
return value.toString();
}
}
/**
* Return the ExternalContext
instance for the current
* request.
*/
protected ExternalContext getExternalContext() {
return (FacesContext.getCurrentInstance().getExternalContext());
}
/**
* Return the FacesContext
instance for the current
* request.
*/
protected FacesContext getFacesContext() {
return (FacesContext.getCurrentInstance());
}
/**
* Retrieve the submitted value from the request parameters for
* this request. The default implementation retrieves the parameter
* value that corresponds to the client identifier of this component.
*
* @param context FacesContext
for the current request
* @param component UIComponent
whose
* submitted value is to be retrieved
*/
protected Object getSubmittedValue(FacesContext context, UIComponent component) {
String clientId = component.getClientId(context);
Map parameters = context.getExternalContext().getRequestParameterMap();
return parameters.get(clientId);
}
/**
* Return true
if the specified component is disabled.
*
* @param component UIComponent
to be checked
*/
protected boolean isDisabled(UIComponent component) {
Object disabled = component.getAttributes().get("disabled");
if (disabled == null) {
return (false);
}
if (disabled instanceof String) {
return (Boolean.valueOf((String) disabled).booleanValue());
} else {
return (disabled.equals(Boolean.TRUE));
}
}
/**
* Return true
if we are we running in a portlet
* environment, as opposed to a servlet based web application.
*
* @param context FacesContext
for the current request
*/
protected boolean isPortlet(FacesContext context) {
return false; // TODO - implement a dynamic check
}
/**
* Return true
if the specified component is read only.
*
* @param component UIComponent
to be checked
*/
protected boolean isReadOnly(UIComponent component) {
Object readonly = component.getAttributes().get("readonly");
if (readonly == null) {
return (false);
}
if (readonly instanceof String) {
return (Boolean.valueOf((String) readonly).booleanValue());
} else {
return (readonly.equals(Boolean.TRUE));
}
}
/**
* Render the element attributes for the generated markup related to this
* component. Simple renderers that create a single markup element
* for this component should override this method and include calls to
* to writeAttribute()
and writeURIAttribute
* on the specified ResponseWriter
.
*
* The default implementation does nothing.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
*
* @exception IOException if an input/output error occurs
*/
protected void renderAttributes(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
}
/**
* Render the element end for the generated markup related to this
* component. Simple renderers that create a single markup element
* for this component should override this method and include a call
* to endElement()
on the specified
* ResponseWriter
.
*
* The default implementation does nothing.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
*
* @exception IOException if an input/output error occurs
*/
protected void renderEnd(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
}
/**
* Render the specified markup to the current response.
*
* @param context FacesContext
for the current request
* @param component UIComponent
associated with this markup
* @param writer ResponseWriter
to which the markup
* should be rendered
* @param markup {@link Markup} to be rendered
*/
protected void renderMarkup(FacesContext context, UIComponent component,
ResponseWriter writer, Markup markup)
throws IOException {
writer.write(markup.getMarkup());
}
/**
* Render the element start for the generated markup related to this
* component. Simple renderers that create a single markup element
* for this component should override this method and include a call
* to startElement()
on the specified
* ResponseWriter
.
*
* The default implementation does nothing.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
* @param writer ResponseWriter
to which the element
* start should be rendered
*
* @exception IOException if an input/output error occurs
*/
protected void renderStart(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
}
/**
* If a submitted value was included on this request, store it in the
* component as appropriate.
*
* The default implementation determines whether this component
* implements EditableValueHolder
. If so, it checks for a
* request parameter with the same name as the clientId
* of this UIComponent
. If there is such a parameter, its
* value is passed (as a String) to the setSubmittedValue()
* method on the EditableValueHolder
component.
*
* @param context FacesContext
for the current request
* @param component EditableValueHolder
component whose
* submitted value is to be stored
*/
protected void setSubmittedValue(FacesContext context, UIComponent component) {
if (!(component instanceof EditableValueHolder)) {
return;
}
component.getAttributes().put("submittedValue", // NOI18N
getSubmittedValue(context, component));
}
}