com.sun.webui.jsf.renderkit.html.RadioButtonRenderer Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007-2018 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
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.RadioButton;
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.ThemeUtilities;
/**
*
* The RadioButtonRenderer
renders a
* {@link com.sun.webui.jsf.component.RadioButton} component.
*
* Encoding
*
* The RadioButtonRenderer
renders a RadioButton
as:
*
* - An INPUT element of type radio for each radio button.
*
* - 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
* radio button are identified by java
* constants defined in the {@link ThemeStyles} class.
*
*
* - RADIOBUTTON for the INPUT element
* - RADIOBUTTON_DISABLED for the INPUT element of a disabled radio
* button
* - RADIOBUTTON_LABEL for label component if a label is rendered
* - RADIOBUTTON_LABEL_DISABLED for a label component of a disabled
* radio button, if a label is rendered
* - RADIOBUTTON_IMAGE for an image component if an image is rendered
* - RADIOBUTTON_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
* RadioButton
component and the optional image and label
* components.
*
* For more details on the encoding the
* RadioButton
component see the super class
* {@link com.sun.webui.jsf.renderkit.html.RbCbRendererBase}
*
*
*
Decoding
*
* If the INPUT element representing a radio button 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.
*
*
* 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
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 the request parameter's 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.
*
* Since the RadioButtonRenderer
only renders a single
* RadioButton
component it cannot enforce that at least
* one radio button should be selected among a group of RadioButton
* components with the same name
property.
*
*
* If the RadioButton
is selected, the selected
* property will be the same value as the selectedValue
* property. If more than one RadioButton
component is
* encoded with the same name
property and more than one
* RadioButton
's is selectred,
* the last selected RadioButton
component that is encoded
* will be appear as checked in the HTML page. Subsequently during the
* next submit, only the checked RadioButton
component
* will be selected.
*
*/
@Renderer(@Renderer.Renders(componentFamily = "com.sun.webui.jsf.RadioButton"))
//FIXME check about making RbCbRendererBase a public abstract class
public class RadioButtonRenderer extends RbCbRendererBase {
private final String MSG_COMPONENT_NOT_RADIOBUTTON =
"RadioButtonRenderer only renders RadioButton components.";
/**
* Creates a new instance of RadioButtonRenderer
*/
public RadioButtonRenderer() {
super();
}
/**
* Decode the RadioButton
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 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 RadioButton
* component to be decoded.
*/
@Override
public void decode(FacesContext context, UIComponent component) {
// We need to know the last state of the component before decoding
// this radio button. This disabled check is not to determine
// if the radio button was disabled on the client.
// We assume that the disabled state is in the same state as it was
// when this radio button was last rendered.
// If the radio button 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 radio button was selected.
// No radio buttons 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 radio button.
//
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.
//
RadioButton radioButton = (RadioButton) component;
String name = radioButton.getName();
boolean inGroup = name != null;
// If name is null use the clientId.
//
if (name == null) {
name = component.getClientId(context);
}
Map requestParameterMap = context.getExternalContext().
getRequestParameterMap();
// The request parameter map contains the INPUT element
// name attribute value as a parameter. The value is the
// the "selectedValue" value of the RadioButton component.
//
if (requestParameterMap.containsKey(name)) {
String newValue = (String) requestParameterMap.get(name);
// We need to discern the case where the radio button
// is part of a group and it is a boolean radio button.
// If the radio button is part of a group and it is a
// boolean radio button then the submitted value contains the
// value of "component.getClientId()". If
// the value was not a unique value within the group
// of boolean radio buttons, then all will appear selected,
// since name will be the same for all the radio buttons
// and the submitted value would always be "true" and then
// every radio button component in the group would decode
// as selected. Due to the HTML implementation of radio
// buttons, only the last radio button will appear selected.
//
Object selectedValue = radioButton.getSelectedValue();
String selectedValueAsString = null;
if (inGroup && selectedValue instanceof Boolean) {
selectedValueAsString = component.getClientId(context);
// Use the toString value of selectedValue even if
// it is a Boolean control, in case the application
// wants "FALSE == FALSE" to mean checked.
//
if (selectedValueAsString.equals(newValue)) {
((UIInput) component).setSubmittedValue(
new String[]{selectedValue.toString()});
return;
}
} else {
selectedValueAsString =
ConversionUtilities.convertValueToString(component,
selectedValue);
if (selectedValueAsString.equals(newValue)) {
((UIInput) component).setSubmittedValue(
new String[]{newValue});
return;
}
}
// Not selected possibly deselected.
//
((UIInput) component).setSubmittedValue(new String[0]);
}
return;
}
/**
* Ensure that the component to be rendered is a RadioButton 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 RadioButton component.
// This message should be logged.
//
if (!(component instanceof RadioButton)) {
throw new IllegalArgumentException(MSG_COMPONENT_NOT_RADIOBUTTON);
}
}
/**
* RadioButtonRenderer renders the entire RadioButton
* component within the renderEnd method.
*
* @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, "radio");
}
/**
* 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 ((RadioButton) component).isChecked();
}
protected String[] styles = {
ThemeStyles.RADIOBUTTON, /* INPUT */
ThemeStyles.RADIOBUTTON_DISABLED, /* INPUT_DIS */
ThemeStyles.RADIOBUTTON_LABEL, /* LABEL */
ThemeStyles.RADIOBUTTON_LABEL_DISABLED, /* LABEL_DIS */
ThemeStyles.RADIOBUTTON_IMAGE, /* IMAGE */
ThemeStyles.RADIOBUTTON_IMAGE_DISABLED, /* IMAGE_DIS */
ThemeStyles.RADIOBUTTON_SPAN, /* SPAN */
ThemeStyles.RADIOBUTTON_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;
}
}