com.sun.webui.jsf.renderkit.html.CheckboxRenderer 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 com.sun.faces.annotation.Renderer;
import java.io.IOException;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.sun.webui.jsf.component.Checkbox;
import com.sun.webui.theme.Theme;
import com.sun.webui.jsf.theme.ThemeStyles;
import com.sun.webui.jsf.util.ConversionUtilities;
import com.sun.webui.jsf.util.MessageUtil;
import com.sun.webui.jsf.util.ThemeUtilities;
/**
*
* The CheckboxRenderer
renders a
* {@link com.sun.webui.jsf.component.Checkbox} component.
*
* Encoding
*
* The CheckboxRenderer
renders a Checkbox
as:
*
* - An INPUT element of type checkbox for each checkbox.
*
* - An optional image. The component rendered for this feature is obtained
* from a call to
getImageComponent()
on the component being
* rendered.
* - An optional label. The component rendered for this feature is obtained
* from a call to
getLabelComponent()
on the component being
* rendered.
*
*
*
*
* The CSS selectors for the elements and components that comprise a
* checkbox are identified by java
* constants defined in the {@link ThemeStyles} class.
*
*
* - CHECKBOX for the INPUT element
* - CHECKBOX_DISABLED for the INPUT element of a disabled radio
* button
* - CHECKBOX_LABEL for label component if a label is rendered
* - CHECKBOX_LABEL_DISABLED for a label component of a disabled
* radio button, if a label is rendered
* - CHECKBOX_IMAGE for an image component if an image is rendered
* - CHECKBOX_IMAGE_DISABLED for an image component of a disabled
* radio button if an image is rendered.
*
* Note that these selectors are appended to any existing selectors
* that may already exist on the styleClass
property of the
* Checkbox
component and the optional image and label
* components.
*
* For more details on the logic for actual renderering of HTML for
* a Checkbox
component see the super class
* {@link com.sun.webui.jsf.renderkit.html.RbCbRendererBase}
*
*
*
Decoding
*
* If the INPUT element representing a checkbox is selected on the
* the client, the submitted request will contain a request parameter
* whose name is the value of the name attribute of the selected
* HTML INPUT element. The value of the request parameter will be the
* value of the value attribute of the selected HTML INPUT element.
* If more than one checkbox INPUT element has the same value for
* the name attribute, then the value of the request parameter will
* be an array of those INPUT element values.
*
*
* The component being decoded is selected if the component's
* isDisabled
and isReadOnly
methods
* return false and:
*
*
* - a request parameter exists that is equal to its
name
* property. If the name
property is null, then a
* request parameter exists that is equal to its clientId
* property.
*
*
*
* And
*
*
* - the request parameter's value is an array that contains an element
* that is
String.equal
to the
* the component's selectedValue
property, after conversion
* to a String
, by calling
* ConversionUtilities.convertValueToString
. If the component
* was encoded as a boolean control, then an element the request parameter's
* array value must be equal to the component's clientId
property.
*
*
*
* If selected, a String[1]
array is assigned as the component's
* submitted value where the single array element is the String
* version of the selectedValue
property or "true" if the
* component was encoded as a boolean control.
* If not selected, a String[0]
array is assigned as the
* component's submitted value or a String[1]
array where the
* single array element is "false" if the component was encoded as a
* boolean control.
*
*
* If the component's isDisabled
or isReadOnly
* methods return true no submitted value is assigned to the component,
* and results in a null submitted value implying the component
* was not submitted, and the state of the component is unchanged.
*
*/
@Renderer(@Renderer.Renders(componentFamily = "com.sun.webui.jsf.Checkbox"))
//FIXME check about making SelectGroupRenderer a public abstract class
public class CheckboxRenderer extends RbCbRendererBase {
private final String MSG_COMPONENT_NOT_CHECKBOX =
"CheckboxRenderer only renders Checkbox components.";
/**
* Creates a new instance of CheckboxRenderer
*/
public CheckboxRenderer() {
super();
}
/**
* Decode the Checkbox
selection.
*
* If the component's isDisabled
and isReadOnly
* methods return false,
* 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 String
value of the
* component's selectedValue
property, the
* radio button is selected. The component's submitted value is
* assigned a String[1]
array where the single array
* element is the matching parameter value.
*
*
* If no matching request parameter or value is found, an instance of
* String[0]
is assigned as the submitted value,
* meaning that this is a component was not selected.
*
*
* If the component was encoded as a boolean control the
* value of the matching request attribute will be the component's
* clientId
property if selected. If selected the
* submitted value is new String[] { "true" }
* and new String[] { "false" }
if not selected.
*
*
* It is the developer's responsibility to ensure the validity of the
* name
property (the name attribute on the
* INPUT element) by ensuring that it is unique to the radio buttons
* for a given group within a form.
*
*
* @param context FacesContext for the request we are processing.
* @param component The Checkbox
* component to be decoded.
*/
@Override
public void decode(FacesContext context, UIComponent component) {
// We need to know if the last state of the component before decoding
// this checkbox. This disabled check is not to determine
// if the checkbox was disabled on the client.
// We assume that the disabled state is in the same state as it was
// when this checkbox was last rendered.
// If the checkbox was disabled then it can not have changed on
// the client. We ignore the case that it might have been
// enabled in javascript on the client.
// This allows us to distinguish that no checkbox was selected.
// No checkboxes are selected when "isDisabled || isReadOnly -> false
// and no request parameters match the name attribute if part of a
// group or the clientId, if a single checkbox.
//
if (isDisabled(component) || isReadOnly(component)) {
return;
}
// If there is a request parameter that that matches the
// name property, this component is one of the possible
// selections. We need to match the value of the parameter to the
// the component's value to see if this is the selected component,
// unless it is a group of Boolean checkboxes.
//
Checkbox checkbox = (Checkbox) component;
String name = checkbox.getName();
boolean inGroup = name != null;
// If name not set look for clientId.
// Boolean checkboxes decode correctly when they are not
// in a group, since the submitted attribute
// value in the clientId and is unique for each check box.
//
if (name == null) {
name = component.getClientId(context);
}
Map requestParameterValuesMap = context.getExternalContext().
getRequestParameterValuesMap();
// If a parameter with key == name does not exist, the component
// was not submitted. This only means that it is
// unchecked, since we already know that is it not in the
// map because is was readonly or disabled. (based on the
// server side state)
//
if (requestParameterValuesMap.containsKey(name)) {
String[] newValues = (String[]) requestParameterValuesMap.get(name);
if (newValues != null || newValues.length != 0) {
String selectedValueAsString = null;
Object selectedValue = checkbox.getSelectedValue();
// We need to discern the case where the checkbox
// is part of a group and it is a boolean checkbox.
// If the checkbox is part of a group and it is a
// boolean checkbox then the submitted value contains the
// value of "component.getClientId()". If
// the value was not a unique value within the group
// of boolean checkboxes, then all will appear selected,
// since name will be the same for all the checkboxes
// and the submitted value would always be "true" and then
// every checkbox component in the group would decode
// as selected.
//
if (inGroup && selectedValue instanceof Boolean) {
selectedValueAsString = component.getClientId(context);
// See if one of the values of the attribute
// is equal to the component id.
//
// Use the toString value of selectedValue even if
// Boolean in case it is FALSE and the application
// wants checked to be "FALSE == FALSE"
//
for (int i = 0; i < newValues.length; ++i) {
if (selectedValueAsString.equals(newValues[i])) {
((UIInput) component).setSubmittedValue(
new String[]{selectedValue.toString()});
return;
}
}
} else {
selectedValueAsString =
ConversionUtilities.convertValueToString(
component, selectedValue);
for (int i = 0; i < newValues.length; ++i) {
if (selectedValueAsString.equals(newValues[i])) {
((UIInput) component).setSubmittedValue(
new String[]{newValues[i]});
return;
}
}
}
// Not selected.
// But this results in an update to the model object
// of every checkbox, even if the value is the same.
// However only those that experience a state change issue
// a ValueChangeEvent.
//
((UIInput) component).setSubmittedValue(new String[0]);
return;
}
}
// Not disabled and this checkbox is not selected.
//
((UIInput) component).setSubmittedValue(new String[0]);
return;
}
/**
* Ensure that the component to be rendered is a Checkbox instance.
* Actual rendering occurs during renderEnd
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be decoded.
*/
@Override
public void renderStart(FacesContext context, UIComponent component,
ResponseWriter writer)
throws IOException {
// Bail out if the component is not a Checkbox component.
if (!(component instanceof Checkbox)) {
throw new IllegalArgumentException(
MessageUtil.getMessage(context,
BUNDLE, MSG_COMPONENT_NOT_CHECKBOX));
}
}
/**
* CheckboxRenderer renders the entire Checkbox
* component within the renderEnd method.
* See {@link com.sun.webui.jsf.renderkit.html.RbCbRendererBase} for
* details on encoding a Checkbox
component.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to be decoded.
*/
@Override
public void renderEnd(FacesContext context, UIComponent component,
ResponseWriter writer)
throws IOException {
Theme theme = ThemeUtilities.getTheme(context);
renderSelection(context, component, theme, writer, "checkbox");
}
/**
* Return true if the component
is selected, false
* otherwise.
*
* @param context FacesContext for the request we are processing.
* @param component UIComponent to test for selected
*/
protected boolean isSelected(FacesContext context, UIComponent component) {
return ((Checkbox) component).isChecked();
}
protected String[] styles = {
ThemeStyles.CHECKBOX, /* INPUT */
ThemeStyles.CHECKBOX_DISABLED, /* INPUT_DIS */
ThemeStyles.CHECKBOX_LABEL, /* LABEL */
ThemeStyles.CHECKBOX_LABEL_DISABLED, /* LABEL_DIS */
ThemeStyles.CHECKBOX_IMAGE, /* IMAGE */
ThemeStyles.CHECKBOX_IMAGE_DISABLED, /* IMAGE_DIS */
ThemeStyles.CHECKBOX_SPAN, /* SPAN */
ThemeStyles.CHECKBOX_SPAN_DISABLED, /* SPAN_DIS */};
/**
* Return the style class name for the structural element indicated
* by styleCode
*
* @param theme The Theme for this request.
* @param styleCode identifies the style class for the element about
* to be rendered.
*/
protected String getStyle(Theme theme, int styleCode) {
String style = null;
try {
style = theme.getStyleClass(styles[styleCode]);
} catch (Exception e) {
// Don't care
}
return style;
}
}