com.sun.webui.jsf.renderkit.html.RbCbRendererBase 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.
*/
package com.sun.webui.jsf.renderkit.html;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.ConverterException;
import com.sun.webui.jsf.component.ImageComponent;
import com.sun.webui.jsf.component.Label;
import com.sun.webui.jsf.component.RadioButton;
import com.sun.webui.jsf.component.RbCbSelector;
import com.sun.webui.theme.Theme;
import com.sun.webui.jsf.util.ConversionUtilities;
import com.sun.webui.jsf.util.RenderingUtilities;
/**
*
* The RbCbRendererBase
class is the abstract base class for
* {@link com.sun.webui.jsf.renderkit.html.RadioButtonRenderer} and
* {@link com.sun.webui.jsf.renderkit.html.CheckboxRenderer}.
*
*
* RbCbRendererBase
provides encoding functionality for
* the RadioButtonRenderer
and CheckboxRenderer
.
* This includes an implementation of getConvertedValue
, and
* a method called renderSelection
which a subclass calls to render
* either a Checkbox
or a RadioButton
component
* at the appropriate time.
* The renderer subclass must implement
*
*
* isSelected
in order for this class to generically render
* either component
* getStyle
so the specific subclass can specify the
* appropriate ThemeStyle
constants.
*
*
* Decoding
*
* See {@link com.sun.webui.jsf.renderkit.html.RadioButtonRenderer} and
* {@link com.sun.webui.jsf.renderkit.html.CheckboxRenderer} for details on
* decoding requests.
*
* Encoding
*
* The renderer emits the following HTML elements.
*
* - An INPUT element of type specified by the subclass in
*
renderSelection
*
* - An optional {@link com.sun.webui.jsf.component.ImageComponent}
* component is rendered for each INPUT element
*
* - An optional {@link com.sun.webui.jsf.component.Label} component
* is rendered for each INPUT element
*
*
*
*
* The ID attributes for HTML elements are constructed as follows,
* where <cid> is the clientId
of the
* component being rendered.
*
*
* <cid> for the INPUT element
*
* <cid>_image for the image component
*
* <cid>_label for the label component
*
*
*
Encoding the INPUT element
*
* If the name
property of the component is null
* the name attribute of the INPUT element will be set to the
* value of the component's clientId
and the control will
* not be part of a group, and behave as an individual control.
* If the name
property is not null
then its
* value is used as the value of the name attribute of the HTML INPUT
* element and the control will behave as part of a group.
*
*
* The ConversionUtilities.getValueAsString
method is called with
* the value of the component's selectedValue
property
* and the result is used as the value of the HTML INPUT element's
* value attribute. The String
value that is returned may be
* the actual value of the selectedValue
property or
* the result of a conversion of a developer defined object value
* to a String
or "true" if the selectedValue
* property was null or never assigned a value.The components
* {@link com.sun.webui.jsf.component.RadioButton} and
* {@link com.sun.webui.jsf.component.Checkbox} implement the behavior
* of returning "true" when selectedValue
is null. Therefore
* if the component parameter is not one of these classes then this behavior
* may vary.
*
*
* If isSelected
returns true
the
* the value of the HTML INPUT element's checked attribute is set to "checked",
* otherwise the checked attribute is not rendered.
*
*
* The following component properties are obtained and rendered in turn and
* equivalent to the HTML INPUT element's attributes of the same name, but
* rendered in all lowercase.
*
* - disabled
* - readOnly
* - tabIndex
* - style
*
* The component's toolTip
property if not null is rendered as the
* value of the HTML INPUT element's title attribute.
* The HTML INPUT element's class attribute is set to the
* component's styleClass
property appended with
* the value returned from a call to the getStyle
method.
*
* Rendering the image component
*
* The renderer calls the component's getImageComponent
* method to obtain an instance of a
* {@link com.sun.webui.jsf.component.ImageComponent} component. If
* null is returned, no image will appear with the control.
* If a non null instance is returned, the appropriate disabled or
* enabled style class returned by getStyle
* is appended to the image's styleClass
property.
* RenderingUtilities.renderComponent
is called to render
* the component.
*
*
* If an image is rendered it appears to the immediate left of the
* control.
*
* Encoding the label component
*
* The renderer calls the component's getLabelComponent
* method to obtain an instance of a
* {@link com.sun.webui.jsf.component.Label} component. If
* null is returned, no label will appear with the control.
* If a non null instance is returned, the appropriate disabled or
* enabled style class returned by getStyle
* is appended to the label's styleClass
property.
* RenderingUtilities.renderComponent
is called to render
* the component.
*
*/
//FIXME this should probably be public
abstract class RbCbRendererBase extends AbstractRenderer {
/**
* The define constant indicating the style class
* for an INPUT element.
*/
protected final static int INPUT = 0;
/**
* The define constant indicating the style class
* for a disabled INPUT element.
*/
protected final static int INPUT_DIS = 1;
/**
* The define constant indicating the style class
* for the LABEL element.
*/
protected final static int LABEL = 2;
/**
* The define constant indicating the style class
* for a disabled LABEL element.
*/
protected final static int LABEL_DIS = 3;
/**
* The define constant indicating the style class
* for the IMG element.
*/
protected final static int IMAGE = 4;
/**
* The define constant indicating the style class
* for a disabled IMG element.
*/
protected final static int IMAGE_DIS = 5;
/**
* The define constant indicating the style class for
* for the containing span element
*/
protected final static int SPAN = 6;
/**
* The define constant indicating the style class for
* for the containing span element, when disabled.
*/
protected final static int SPAN_DIS = 7;
// Collect most NOI18N in one place
//
private static final String INPUT_ELEM = "input"; //NOI18N
private static final String SPAN_ELEM = "span"; //NOI18N
private static final String CHECKED_ATTR = "checked"; //NOI18N
private static final String DISABLED_ATTR = "disabled"; //NOI18N
private static final String CLASS_ATTR = "class"; //NOI18N
private static final String ID_ATTR = "id"; //NOI18N
private static final String NAME_ATTR = "name"; //NOI18N
private static final String READONLY_ATTR = "readonly"; //NOI18N
private static final String READONLY_CC_ATTR = "readOnly"; //NOI18N
private static final String STYLE_ATTR = "style"; //NOI18N
private static final String STYLECLASS_ATTR = "styleClass"; //NOI18N
private static final String TABINDEX_ATTR = "tabindex"; //NOI18N
private static final String TABINDEX_CC_ATTR = "tabIndex"; //NOI18N
private static final String TITLE_ATTR = "title"; //NOI18N
private static final String TOOLTIP_ATTR = "toolTip"; //NOI18N
private static final String TYPE_ATTR = "type"; //NOI18N
private static final String VALUE_ATTR = "value"; //NOI18N
private static final String SPAN_SUFFIX = "_span"; //NOI18N
/**
* The list of attribute names for Rb and Cb
*
**/
public static final String RBCB_EVENTS_ATTRIBUTES[] = {"onFocus", "onBlur", "onClick", "onDblClick", "onChange", // NOI18N
"onMouseDown", "onMouseUp", "onMouseOver", "onMouseMove", "onMouseOut", // NOI18N
"onKeyPress", "onKeyDown", "onKeyUp", // NOI18N
};
/**
* Creates a new instance of RbCbRendererBase
*/
public RbCbRendererBase() {
super();
}
/**
* The getStlye method is implemented by subclasses
* to return the actual CSS style class name for
* the given structural element of the rendered component.
*
* @param theme Theme for the request we are processing.
* @param styleCode one of the previously defined constants.
*/
protected abstract String getStyle(Theme theme, int styleCode);
/**
* Implemented in the subclass to determine if the item
* is the currently selected control.
*
* @param Object selectedValue contol value.
* @param currentValue the value of the currently selected control.
*/
protected abstract boolean isSelected(FacesContext context,
UIComponent component);
/*
*
* Decode the RadioButton
or Checkbox
selection.
* If the value of the component's name
property
* has been set, the value is used to match a request parameter.
* If it has not been set the component clientId is used to match
* a request parameter. If a match is found, and the value of the
* of the request parameter matches the value of the
* selectedValue
component property, the
* value of the selectedValue
property is set
* as the submitted value, as a one element array containing this value.
*
*
* In the case of a Checkbox
component where the
* check box is part of a group, the value of the request parameter
* may contain more than one value. If the value of the component's
* selectedValue
property is among the returned values
* then the value of the selectedValue
property is
* set as the submitted value in a one element array.
* In the case of a RadioButton
* there is always only one element selected when part of a group.
*
*
* If no matching request parameter is found, an instance of
* String[0]
is assigned as the submitted value,
* meaning that this is a component was not selected.
*
*
* @param context FacesContext for the request we are processing.
* @param component The RadioButton
or Checkbox
* component to be decoded.
*/
// This should probably be in RadioButtonRenderer.
//
/**
* Render the child components of this UIComponent, following the rules
* described for encodeBegin() to acquire the appropriate value to be
* rendered. This method will only be called if the rendersChildren property
* of this component is true.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be decoded.
*/
public void encodeChildren(FacesContext context, UIComponent component)
throws IOException {
}
/**
* Render a radio button or a checkbox.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be decoded.
* @param writer ResponseWriter
to which the HTML will
* be output
* @param type the INPUT element type attribute value.
*/
protected void renderSelection(FacesContext context,
UIComponent component, Theme theme, ResponseWriter writer,
String type) throws IOException {
// Contain the radio button components within a span element
// assigning the style and styleClass attribute to its
// style and class attributes.
//
writer.startElement(SPAN_ELEM, component);
writer.writeAttribute(ID_ATTR,
component.getClientId(context).concat(SPAN_SUFFIX), null);
// Transfer explicit style attribute value to the span's style
//
String prop = (String) ((RbCbSelector) component).getStyle();
if (prop != null) {
writer.writeAttribute(STYLE_ATTR, prop, STYLE_ATTR);
}
// Merge the standard style class with the styleClass
// attribute
//
String styleClass = getStyle(theme,
((RbCbSelector) component).isDisabled() ? SPAN_DIS : SPAN);
styleClass = RenderingUtilities.getStyleClasses(context, component,
styleClass);
if (styleClass != null) {
writer.writeAttribute(CLASS_ATTR, styleClass, null);
}
renderInput(context, component, theme, writer, type);
renderImage(context, component, theme, writer);
renderLabel(context, component, theme, writer);
writer.endElement(SPAN_ELEM);
}
/**
* Called from renderSelection to render an INPUT element of type
* type
for the specified component
.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be rendered.
* @param writer ResponseWriter
to which the HTML will
* be output
* @param type the INPUT element type attribute value.
*/
protected void renderInput(FacesContext context,
UIComponent component, Theme theme, ResponseWriter writer,
String type) throws IOException {
RbCbSelector rbcbSelector = (RbCbSelector) component;
String componentId = component.getClientId(context);
writer.startElement(INPUT_ELEM, component);
writer.writeAttribute(TYPE_ATTR, type, null);
// Set the control name to the radiobutton group id
// and create a unique id from the radiobutton group id.
//
writer.writeAttribute(ID_ATTR, componentId, ID_ATTR);
// If name is not set use the component's clientId
//
boolean inGroup = true;
String prop = rbcbSelector.getName();
if (prop == null) {
prop = componentId;
inGroup = false;
}
writer.writeAttribute(NAME_ATTR, prop, NAME_ATTR);
// If the selectedValue is Boolean and the component is part
// of a group, "name != null", then set the value of the value
// attribute to "component.getClientId()".
//
Object selectedValue = rbcbSelector.getSelectedValue();
prop = ConversionUtilities.convertValueToString(component,
selectedValue);
// Need to check immediate conditions
// submittedValue will be non null if immediate is true on
// some action component or a component on the page was invalid
//
String[] subValue = (String[]) rbcbSelector.getSubmittedValue();
if (subValue == null) {
Object selected = rbcbSelector.getSelected();
if (isSelected(context, component)) {
writer.writeAttribute(CHECKED_ATTR, CHECKED_ATTR, null);
}
// A component can't be selected if "getSelected" returns null
//
// Remember that the rendered value was null.
//
ConversionUtilities.setRenderedValue(component, selected);
} else //
// if the submittedValue is a 0 length array or the
// first element is "" then the control is unchecked.
//
if (subValue.length != 0 && subValue[0].length() != 0) {
// The submitted value has the String value of the
// selectedValue property. Just compare the submittedValue
// to it to determine if it is checked.
//
// Assume that the RENDERED_VALUE_STATE is the same
// as the last rendering.
//
if (prop != null && prop.equals(subValue[0])) {
writer.writeAttribute(CHECKED_ATTR, CHECKED_ATTR, null);
}
}
// If not ingroup prop has String version of selectedValue
//
boolean booleanControl = selectedValue instanceof Boolean;
if (inGroup && booleanControl) {
prop = componentId;
}
writer.writeAttribute(VALUE_ATTR, prop, null);
boolean readonly = rbcbSelector.isReadOnly();
if (readonly) {
writer.writeAttribute(READONLY_ATTR, READONLY_ATTR,
READONLY_CC_ATTR);
}
String styleClass = null;
boolean disabled = rbcbSelector.isDisabled();
if (disabled) {
writer.writeAttribute(DISABLED_ATTR, DISABLED_ATTR, DISABLED_ATTR);
styleClass = getStyle(theme, INPUT_DIS);
} else {
styleClass = getStyle(theme, INPUT);
}
prop = rbcbSelector.getToolTip();
if (prop != null) {
writer.writeAttribute(TITLE_ATTR, prop, TOOLTIP_ATTR);
}
// Output the component's event attributes
// Probably want the 'no auto submit javascript at some point'
//
addStringAttributes(context, component, writer, RBCB_EVENTS_ATTRIBUTES);
int tabIndex = rbcbSelector.getTabIndex();
if (tabIndex > 0 && tabIndex < 32767) {
writer.writeAttribute(TABINDEX_ATTR,
String.valueOf(tabIndex), TABINDEX_CC_ATTR);
}
writer.endElement(INPUT_ELEM);
}
/**
* Called from renderSelection to render an IMG element for the
* specified item
control.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be decoded.
* @param writer ResponseWriter
to which the HTML will
* be output
*/
protected void renderImage(FacesContext context,
UIComponent component, Theme theme,
ResponseWriter writer) throws IOException {
UIComponent imageComponent = getImageComponent(context, component,
theme);
if (imageComponent != null) {
RenderingUtilities.renderComponent(imageComponent, context);
}
}
// There is a serious issue creating child components for
// renderering purposes. They must be updated to reflect
// the application state. This can happen in two ways.
// Literal property values in the current component being rendered
// that are intended for the child component may have been
// changed by the application.
// Properties intended for the child component may be binding
// expressions in which case the value binding must be
// assigned to the property in the child component.
// Since both these values must be updated in the child component
// since the application can change them at any time, it makes
// sense to just always update the child with the value obtained
// from the accessor for the property vs. obtaining and assigning
// the ValueBinding.
//
// Also child creation should occur in the component and
// the process of this creation should produce a facet
// so that the renderer just asks for the facet.
// It may orginate from the component or the developer.
//
private UIComponent getImageComponent(FacesContext context,
UIComponent component, Theme theme) throws IOException {
RbCbSelector rbcbComponent = (RbCbSelector) component;
ImageComponent imageComponent =
(ImageComponent) rbcbComponent.getImageComponent();
if (imageComponent == null) {
return null;
}
// Need to apply disabled class
//
String styleClass = getStyle(theme,
rbcbComponent.isDisabled() ? IMAGE_DIS : IMAGE);
styleClass = RenderingUtilities.getStyleClasses(context,
imageComponent, styleClass);
if (styleClass != null) {
imageComponent.setStyleClass(styleClass);
}
return imageComponent;
}
/**
* Called from renderSelection
to render a LABEL.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be decoded.
* @param writer ResponseWriter
to which the HTML will
* be output
*/
protected void renderLabel(FacesContext context, UIComponent component,
Theme theme, ResponseWriter writer) throws IOException {
UIComponent labelComponent = getLabelComponent(context, component,
theme);
if (labelComponent != null) {
RenderingUtilities.renderComponent(labelComponent, context);
}
}
private UIComponent getLabelComponent(FacesContext context,
UIComponent component, Theme theme) throws IOException {
RbCbSelector rbcbComponent = (RbCbSelector) component;
Label labelComponent = (Label) rbcbComponent.getLabelComponent();
if (labelComponent == null) {
return null;
}
// Need to apply disabled class
//
String styleClass = getStyle(theme,
rbcbComponent.isDisabled() ? LABEL_DIS : LABEL);
styleClass = RenderingUtilities.getStyleClasses(context,
labelComponent, styleClass);
if (styleClass != null) {
labelComponent.setStyleClass(styleClass);
}
return labelComponent;
}
/**
*
* Attempt to convert previously stored state information into an
* object of the type required for this component (optionally using the
* registered {@link javax.faces.convert.Converter} for this component,
* if there is one). If conversion is successful, the new value
* is returned and if not, a
* {@link javax.faces.convert.ConverterException} is thrown.
*
*
* @param context {@link FacesContext} for the request we are processing
* @param component component being renderer.
* @param submittedValue a value stored on the component during
* decode
.
*
* @exception ConverterException if the submitted value
* cannot be converted successfully.
* @exception NullPointerException if context
* or component
is null
*/
public Object getConvertedValue(FacesContext context,
UIComponent component,
Object submittedValue)
throws ConverterException {
// I know this looks odd but it gives an opportunity
// for an alternative renderer for Checkbox and RadioButton
// to provide a converter.
//
return ((RbCbSelector) component).getConvertedValue(context,
(RbCbSelector) component, submittedValue);
}
}