com.sun.faces.renderkit.html_basic.MenuRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.faces.renderkit.html_basic.MenuRenderer.java

Source

/*
 * 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://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
 */

/*
 * $Id: MenuRenderer.java,v 1.51.28.1.2.1 2006/04/12 19:32:25 ofung Exp $
 *
 * (C) Copyright International Business Machines Corp., 2001,2002
 * The source code for this program is not published or otherwise
 * divested of its trade secrets, irrespective of what has been
 * deposited with the U. S. Copyright Office.   
 */

// MenuRenderer.java

package com.sun.faces.renderkit.html_basic;

import com.sun.faces.util.Util;
import com.sun.faces.RIConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.component.UIComponent;
import javax.faces.component.UISelectMany;
import javax.faces.component.UISelectOne;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.ValueBinding;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * <B>MenuRenderer</B> is a class that renders the current value of
 * <code>UISelectOne<code> or <code>UISelectMany<code> component as a list of
 * menu options.
 */

public class MenuRenderer extends HtmlBasicInputRenderer {

    //
    // Protected Constants
    //

    //
    // Class Variables
    //

    private static final Log log = LogFactory.getLog(MenuRenderer.class);

    //
    // Instance Variables
    //

    // Attribute Instance Variables

    // Relationship Instance Variables

    //
    // Constructors and Initializers    
    //

    public MenuRenderer() {
        super();
    }

    //
    // Class methods
    //

    //
    // General Methods
    //

    //
    // Methods From Renderer
    //

    public void decode(FacesContext context, UIComponent component) {
        if (context == null || component == null) {
            throw new NullPointerException(Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID));
        }
        if (log.isTraceEnabled()) {
            log.trace("Begin decoding component " + component.getId());
        }

        // If the component is disabled, do not change the value of the
        // component, since its state cannot be changed.
        if (Util.componentIsDisabledOnReadonly(component)) {
            if (log.isTraceEnabled()) {
                log.trace("No decoding necessary since the component " + component.getId() + " is disabled");
            }
            return;
        }

        String clientId = component.getClientId(context);
        Util.doAssert(clientId != null);
        // currently we assume the model type to be of type string or 
        // convertible to string and localised by the application.
        if (component instanceof UISelectMany) {
            Map requestParameterValuesMap = context.getExternalContext().getRequestParameterValuesMap();
            if (requestParameterValuesMap.containsKey(clientId)) {
                String newValues[] = (String[]) requestParameterValuesMap.get(clientId);
                setSubmittedValue(component, newValues);
                if (log.isTraceEnabled()) {
                    log.trace("submitted values for UISelectMany component " + component.getId()
                            + " after decoding " + newValues);
                }
            } else {
                // Use the empty array, not null, to distinguish
                // between an deselected UISelectMany and a disabled one
                setSubmittedValue(component, new String[0]);
                if (log.isTraceEnabled()) {
                    log.trace(
                            "Set empty array for UISelectMany component " + component.getId() + " after decoding ");
                }
            }
        } else {
            // this is a UISelectOne
            Map requestParameterMap = context.getExternalContext().getRequestParameterMap();
            if (requestParameterMap.containsKey(clientId)) {
                String newValue = (String) requestParameterMap.get(clientId);
                setSubmittedValue(component, newValue);
                if (log.isTraceEnabled()) {
                    log.trace("submitted value for UISelectOne component " + component.getId() + " after decoding "
                            + newValue);
                }

            } else {
                // there is no value, but this is different from a null
                // value.
                setSubmittedValue(component, RIConstants.NO_VALUE);
            }
        }
        return;
    }

    public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)
            throws ConverterException {
        if (component instanceof UISelectMany) {
            return convertSelectManyValue(context, ((UISelectMany) component), (String[]) submittedValue);
        } else {
            return convertSelectOneValue(context, ((UISelectOne) component), (String) submittedValue);
        }
    }

    public Object convertSelectOneValue(FacesContext context, UISelectOne uiSelectOne, String newValue)
            throws ConverterException {
        Object convertedValue = null;
        if (newValue == RIConstants.NO_VALUE) {
            return null;
        }
        if (newValue == null) {
            if (log.isTraceEnabled()) {
                log.trace("No conversion necessary for SelectOne Component  " + uiSelectOne.getId()
                        + " since the new value is null ");
            }
            return null;
        }

        convertedValue = super.getConvertedValue(context, uiSelectOne, newValue);
        if (log.isTraceEnabled()) {
            log.trace("SelectOne Component  " + uiSelectOne.getId() + " convertedValue " + convertedValue);
        }
        return convertedValue;
    }

    public Object convertSelectManyValue(FacesContext context, UISelectMany uiSelectMany, String[] newValues)
            throws ConverterException {
        // if we have no local value, try to get the valueBinding.
        ValueBinding valueBinding = uiSelectMany.getValueBinding("value");

        Object result = newValues; // default case, set local value
        Class modelType = null;
        boolean throwException = false;

        // If we have a ValueBinding
        if (null != valueBinding) {
            modelType = valueBinding.getType(context);
            // Does the valueBinding resolve properly to something with
            // a type?
            if (null != modelType) {
                if (modelType.isArray()) {
                    result = handleArrayCase(context, uiSelectMany, modelType, newValues);
                } else if (List.class.isAssignableFrom(modelType)) {
                    result = handleListCase(context, newValues);
                } else {
                    throwException = true;
                }
            } else {
                throwException = true;
            }
        } else {
            // No ValueBinding, just use Object array.
            Object[] convertedValues = new Object[1];
            result = handleArrayCase(context, uiSelectMany, convertedValues.getClass(), newValues);
        }
        if (throwException) {
            String values = "";
            if (null != newValues) {
                for (int i = 0; i < newValues.length; i++) {
                    values = values + " " + newValues[i];
                }
            }
            Object[] params = { values, valueBinding.getExpressionString() };
            throw new ConverterException(Util.getExceptionMessage(Util.CONVERSION_ERROR_MESSAGE_ID, params));
        }

        // At this point, result is ready to be set as the value
        if (log.isTraceEnabled()) {
            log.trace("SelectMany Component  " + uiSelectMany.getId() + " convertedValues " + result);
        }
        return result;
    }

    protected Object handleArrayCase(FacesContext context, UISelectMany uiSelectMany, Class arrayClass,
            String[] newValues) throws ConverterException {
        Object result = null;
        Class elementType = null;
        Converter converter = null;
        int i = 0, len = (null != newValues ? newValues.length : 0);

        elementType = arrayClass.getComponentType();

        // Optimization: If the elementType is String, we don't need
        // conversion.  Just return newValues.
        if (elementType.equals(String.class)) {
            return newValues;
        }

        try {
            result = Array.newInstance(elementType, len);
        } catch (Exception e) {
            throw new ConverterException(e);
        }

        // bail out now if we have no new values, returning our
        // oh-so-useful zero-length array.
        if (null == newValues) {
            return result;
        }

        // obtain a converter.

        // attached converter takes priority
        if (null == (converter = uiSelectMany.getConverter())) {
            // Otherwise, look for a by-type converter
            if (null == (converter = Util.getConverterForClass(elementType, context))) {
                // if that fails, and the attached values are of Object type,
                // we don't need conversion.
                if (elementType.equals(Object.class)) {
                    return newValues;
                }
                String valueStr = "";
                for (i = 0; i < newValues.length; i++) {
                    valueStr = valueStr + " " + newValues[i];
                }
                Object[] params = { valueStr, "null Converter" };

                throw new ConverterException(Util.getExceptionMessage(Util.CONVERSION_ERROR_MESSAGE_ID, params));
            }
        }

        Util.doAssert(null != result);
        if (elementType.isPrimitive()) {
            for (i = 0; i < len; i++) {
                if (elementType.equals(Boolean.TYPE)) {
                    Array.setBoolean(result, i,
                            ((Boolean) converter.getAsObject(context, uiSelectMany, newValues[i])).booleanValue());
                } else if (elementType.equals(Byte.TYPE)) {
                    Array.setByte(result, i,
                            ((Byte) converter.getAsObject(context, uiSelectMany, newValues[i])).byteValue());
                } else if (elementType.equals(Double.TYPE)) {
                    Array.setDouble(result, i,
                            ((Double) converter.getAsObject(context, uiSelectMany, newValues[i])).doubleValue());
                } else if (elementType.equals(Float.TYPE)) {
                    Array.setFloat(result, i,
                            ((Float) converter.getAsObject(context, uiSelectMany, newValues[i])).floatValue());
                } else if (elementType.equals(Integer.TYPE)) {
                    Array.setInt(result, i,
                            ((Integer) converter.getAsObject(context, uiSelectMany, newValues[i])).intValue());
                } else if (elementType.equals(Character.TYPE)) {
                    Array.setChar(result, i,
                            ((Character) converter.getAsObject(context, uiSelectMany, newValues[i])).charValue());
                } else if (elementType.equals(Short.TYPE)) {
                    Array.setShort(result, i,
                            ((Short) converter.getAsObject(context, uiSelectMany, newValues[i])).shortValue());
                } else if (elementType.equals(Long.TYPE)) {
                    Array.setLong(result, i,
                            ((Long) converter.getAsObject(context, uiSelectMany, newValues[i])).longValue());
                }
            }
        } else {
            for (i = 0; i < len; i++) {
                if (log.isDebugEnabled()) {
                    Object converted = converter.getAsObject(context, uiSelectMany, newValues[i]);
                    log.debug("String value: " + newValues[i] + " converts to : " + converted.toString());
                }
                Array.set(result, i, converter.getAsObject(context, uiSelectMany, newValues[i]));
            }
        }
        return result;
    }

    protected Object handleListCase(FacesContext context, String[] newValues) {
        int i = 0, len = newValues.length;
        ArrayList result = new ArrayList(len);
        for (i = 0; i < len; i++) {
            result.add(newValues[i]);
        }

        return result;
    }

    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
        if (context == null || component == null) {
            throw new NullPointerException(Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID));
        }
    }

    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        if (context == null || component == null) {
            throw new NullPointerException(Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID));
        }
    }

    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {

        if (context == null || component == null) {
            throw new NullPointerException(Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID));
        }

        if (log.isTraceEnabled()) {
            log.trace("Begin encoding component " + component.getId());
        }
        // suppress rendering if "rendered" property on the component is
        // false.
        if (!component.isRendered()) {
            if (log.isTraceEnabled()) {
                log.trace("End encoding component " + component.getId() + " since "
                        + "rendered attribute is set to false ");
            }
            return;
        }

        renderSelect(context, component);
        if (log.isTraceEnabled()) {
            log.trace("End encoding component " + component.getId());
        }
    }

    // Render the "select" portion..
    //
    void renderSelect(FacesContext context, UIComponent component) throws IOException {

        ResponseWriter writer = context.getResponseWriter();
        Util.doAssert(writer != null);

        if (log.isTraceEnabled()) {
            log.trace("Rendering 'select'");
        }
        writer.startElement("select", component);
        writeIdAttributeIfNecessary(context, writer, component);
        writer.writeAttribute("name", component.getClientId(context), "clientId");
        // render styleClass attribute if present.
        String styleClass = null;
        if (null != (styleClass = (String) component.getAttributes().get("styleClass"))) {
            writer.writeAttribute("class", styleClass, "styleClass");
        }
        if (!getMultipleText(component).equals("")) {
            writer.writeAttribute("multiple", "multiple", "multiple");
        }

        // Determine how many option(s) we need to render, and update
        // the component's "size" attribute accordingly;  The "size"
        // attribute will be rendered as one of the "pass thru" attributes
        int itemCount = getOptionNumber(context, component);
        if (log.isTraceEnabled()) {
            log.trace("Rendering " + itemCount + " options");
        }
        // If "size" is *not* set explicitly, we have to default it correctly
        Object size = component.getAttributes().get("size");
        if ((null == size) || ((size instanceof Integer) && ((Integer) size).intValue() == Integer.MIN_VALUE)) {
            writeDefaultSize(writer, itemCount);
        }

        Util.renderPassThruAttributes(writer, component);
        Util.renderBooleanPassThruAttributes(writer, component);

        // Now, render the "options" portion...
        renderOptions(context, component);

        writer.endElement("select");
    }

    int getOptionNumber(FacesContext context, UIComponent component) {
        Iterator items = Util.getSelectItems(context, component);
        int itemCount = 0;
        while (items.hasNext()) {
            itemCount++;
            SelectItem item = (SelectItem) items.next();
            if (item instanceof SelectItemGroup) {
                int optionsLength = ((SelectItemGroup) item).getSelectItems().length;
                itemCount = itemCount + optionsLength;
            }
        }
        return itemCount;
    }

    void renderOptions(FacesContext context, UIComponent component) throws IOException {

        ResponseWriter writer = context.getResponseWriter();
        Util.doAssert(writer != null);

        Iterator items = Util.getSelectItems(context, component);
        SelectItem curItem = null;
        while (items.hasNext()) {
            curItem = (SelectItem) items.next();
            if (curItem instanceof SelectItemGroup) {
                // render OPTGROUP
                writer.startElement("optgroup", component);
                writer.writeAttribute("label", curItem.getLabel(), "label");
                // render options of this group.
                SelectItem[] itemsArray = ((SelectItemGroup) curItem).getSelectItems();
                for (int i = 0; i < itemsArray.length; ++i) {
                    renderOption(context, component, itemsArray[i]);
                }
                writer.endElement("optgroup");
            } else {
                renderOption(context, component, curItem);
            }
        }
    }

    protected void renderOption(FacesContext context, UIComponent component, SelectItem curItem)
            throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        Util.doAssert(writer != null);

        writer.writeText("\t", null);
        writer.startElement("option", component);

        String valueString = getFormattedValue(context, component, curItem.getValue());
        writer.writeAttribute("value", valueString, "value");

        Object submittedValues[] = getSubmittedSelectedValues(context, component);
        boolean isSelected;
        if (submittedValues != null) {
            isSelected = isSelected(valueString, submittedValues);
        } else {
            Object selectedValues = getCurrentSelectedValues(context, component);
            isSelected = isSelected(curItem.getValue(), selectedValues);
        }

        if (isSelected) {
            writer.writeAttribute("selected", "selected", "selected");
        }
        if (curItem.isDisabled()) {
            writer.writeAttribute("disabled", "disabled", "disabled");
        }

        String labelClass = null;
        Boolean disabledAttr = (Boolean) component.getAttributes().get("disabled");
        boolean componentDisabled = false;
        if (disabledAttr != null) {
            if (disabledAttr.equals(Boolean.TRUE)) {
                componentDisabled = true;
            }
        }
        if (componentDisabled || curItem.isDisabled()) {
            labelClass = (String) component.getAttributes().get("disabledClass");
        } else {
            labelClass = (String) component.getAttributes().get("enabledClass");
        }
        if (labelClass != null) {
            writer.writeAttribute("class", labelClass, "labelClass");
        }

        writer.writeText(curItem.getLabel(), "label");
        writer.endElement("option");
        writer.writeText("\n", null);

    }

    boolean isSelected(Object itemValue, Object valueArray) {
        if (null != valueArray) {
            int len = Array.getLength(valueArray);
            for (int i = 0; i < len; i++) {
                Object value = Array.get(valueArray, i);
                if (value == null) {
                    if (itemValue == null) {
                        return true;
                    }
                } else if (value.equals(itemValue)) {
                    return true;
                }
            }
        }
        return false;
    }

    boolean isSelected(Object itemValue, Object[] values) {
        if (null != values) {
            int len = values.length;
            for (int i = 0; i < len; i++) {
                if (values[i].equals(itemValue)) {
                    return true;
                }
            }
        }
        return false;
    }

    protected void writeDefaultSize(ResponseWriter writer, int itemCount) throws IOException {
        // if size is not specified default to 1.
        writer.writeAttribute("size", "1", "size");
    }

    // To derive a selectOne type component from this, override
    // these methods.
    String getMultipleText(UIComponent component) {
        if (component instanceof UISelectMany) {
            return " multiple ";
        }
        return "";
    }

    Object[] getSubmittedSelectedValues(FacesContext context, UIComponent component) {
        if (component instanceof UISelectMany) {
            UISelectMany select = (UISelectMany) component;
            return (Object[]) select.getSubmittedValue();
        }

        UISelectOne select = (UISelectOne) component;
        Object returnObject;
        if (null != (returnObject = select.getSubmittedValue())) {
            return new Object[] { returnObject };
        }
        return null;
    }

    Object getCurrentSelectedValues(FacesContext context, UIComponent component) {
        if (component instanceof UISelectMany) {
            UISelectMany select = (UISelectMany) component;
            Object value = select.getValue();
            if (value instanceof List)
                return ((List) value).toArray();

            return value;
        }

        UISelectOne select = (UISelectOne) component;
        Object returnObject;
        if (null != (returnObject = select.getValue())) {
            return new Object[] { returnObject };
        }
        return null;
    }

} // end of class MenuRenderer