All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.renderkit.html_basic.MenuRenderer Maven / Gradle / Ivy

Go to download

This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.

There is a newer version: 2.4.0
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 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://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/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 packager/legal/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.
 */

/*
 * (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 java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Queue;
import java.util.LinkedList;
import java.util.logging.Level;

import javax.el.ELException;
import javax.el.ValueExpression;
import javax.el.ExpressionFactory;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectMany;
import javax.faces.component.UISelectOne;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import javax.faces.FacesException;

import com.sun.faces.RIConstants;
import com.sun.faces.io.FastStringWriter;
import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.renderkit.SelectItemsIterator;
import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
import com.sun.faces.util.RequestStateManager;
import com.sun.faces.util.ReflectionUtils;

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

public class MenuRenderer extends HtmlBasicInputRenderer {


    private static final Attribute[] ATTRIBUTES =
          AttributeManager.getAttributes(AttributeManager.Key.SELECTMANYMENU);


    // ---------------------------------------------------------- Public Methods


    public Object convertSelectManyValue(FacesContext context,
                                         UISelectMany uiSelectMany,
                                         String[] newValues)
          throws ConverterException {

        // if we have no local value, try to get the valueExpression.
        ValueExpression valueExpression =
              uiSelectMany.getValueExpression("value");

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

        // If we have a ValueExpression
        if (null != valueExpression) {
            Class modelType = valueExpression.getType(context.getELContext());
            // Does the valueExpression resolve properly to something with
            // a type?
            if (modelType != null) {
                result = convertSelectManyValuesForModel(context,
                                                         uiSelectMany,
                                                         modelType,
                                                         newValues);
            }
            // If it could not be converted, as a fall back try the type of
            // the valueExpression's current value covering some edge cases such
            // as where the current value came from a Map.
            if(result == null) {
                Object value = valueExpression.getValue(context.getELContext());
                if(value != null) {
                    result = convertSelectManyValuesForModel(context,
                                                             uiSelectMany,
                                                             value.getClass(),
                                                             newValues);
                }
            }
            if(result == null) {
                throwException = true;
            }
        } else {
            // No ValueExpression, just use Object array.
            result = convertSelectManyValues(context, uiSelectMany,
                                             Object[].class,
                                             newValues);
        }
        if (throwException) {
            StringBuffer values = new StringBuffer();
            if (null != newValues) {
                for (int i = 0; i < newValues.length; i++) {
                    if (i == 0) {
                        values.append(newValues[i]);
                    } else {
                        values.append(' ').append(newValues[i]);
                    }
                }
            }
            Object[] params = {
                  values.toString(),
                  valueExpression.getExpressionString()
            };
            throw new ConverterException
                  (MessageUtils.getExceptionMessage(MessageUtils.CONVERSION_ERROR_MESSAGE_ID,
                                                    params));
        }

        // At this point, result is ready to be set as the value
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("SelectMany Component  " + uiSelectMany.getId() +
                        " convertedValues " + result);
        }
        return result;

    }


    public Object convertSelectOneValue(FacesContext context,
                                        UISelectOne uiSelectOne,
                                        String newValue)
          throws ConverterException {

        if (RIConstants.NO_VALUE.equals(newValue)) {
            return null;
        }
        if (newValue == null) {
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("No conversion necessary for SelectOne Component  "
                            + uiSelectOne.getId()
                            + " since the new value is null ");
            }
            return null;
        }

        Object convertedValue =
              super.getConvertedValue(context, uiSelectOne, newValue);
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("SelectOne Component  " + uiSelectOne.getId() +
                        " convertedValue " + convertedValue);
        }
        return convertedValue;

    }

    @Override
    public void decode(FacesContext context, UIComponent component) {

        rendererParamsNotNull(context, component);

        if (!shouldDecode(component)) {
            return;
        }

        String clientId = decodeBehaviors(context, component);

        if (clientId == null) {
            clientId = component.getClientId(context);
        }
        assert(clientId != null);
        // currently we assume the model type to be of type string or
        // convertible to string and localized by the application.
        if (component instanceof UISelectMany) {
            Map requestParameterValuesMap =
                  context.getExternalContext().
                        getRequestParameterValuesMap();
            if (requestParameterValuesMap.containsKey(clientId)) {
                String newValues[] = requestParameterValuesMap.
                      get(clientId);
                setSubmittedValue(component, newValues);
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("submitted values for UISelectMany component "
                                +
                                component.getId()
                                + " after decoding "
                                + Arrays.toString(newValues));
                }
            } else {
                // Use the empty array, not null, to distinguish
                // between an deselected UISelectMany and a disabled one
                setSubmittedValue(component, new String[0]);
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("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 = requestParameterMap.get(clientId);
                setSubmittedValue(component, newValue);
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("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);
            }
        }

    }


    @Override
    public void encodeBegin(FacesContext context, UIComponent component)
          throws IOException {

        rendererParamsNotNull(context, component);

    }


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

        rendererParamsNotNull(context, component);

        if (!shouldEncode(component)) {
            return;
        }

        renderSelect(context, component);

    }


    @Override
    public Object getConvertedValue(FacesContext context, UIComponent component,
                                    Object submittedValue)
          throws ConverterException {

        if (component instanceof UISelectMany) {
            // need to set the 'TARGET_COMPONENT_ATTRIBUTE_NAME' request attr so the
            // coerce-value call in the jsf-api UISelectMany.matchValue will work
            // (need a better way to determine the currently processing UIComponent ...)
            RequestStateManager.set(context,
                                    RequestStateManager.TARGET_COMPONENT_ATTRIBUTE_NAME,
                                    component);
            return convertSelectManyValue(context,
                                          ((UISelectMany) component),
                                          (String[]) submittedValue);
        } else {
            return convertSelectOneValue(context,
                                         ((UISelectOne) component),
                                         (String) submittedValue);
        }

    }

    // ------------------------------------------------------- Protected Methods


    /*
     * Converts the provided string array and places them into the correct provided model type.
     */
    protected Object convertSelectManyValuesForModel(FacesContext context,
                                                     UISelectMany uiSelectMany,
                                                     Class modelType,
                                                     String[] newValues) {

        if (modelType.isArray()) {
            return convertSelectManyValues(context,
                                           uiSelectMany,
                                           modelType,
                                           newValues);
        } else if (Collection.class.isAssignableFrom(modelType)) {
            Object[] values = (Object[]) convertSelectManyValues(context,
                                                                 uiSelectMany,
                                                                 Object[].class,
                                                                 newValues);

            Collection targetCollection = null;

            // see if the collectionType hint is available, if so, use that
            Object collectionTypeHint = uiSelectMany.getAttributes().get("collectionType");
            if (collectionTypeHint != null) {
                targetCollection = createCollectionFromHint(collectionTypeHint);
            } else {
                // try to get a new Collection to store the values based
                // by trying to create a clone
                Collection currentValue = (Collection) uiSelectMany.getValue();
                if (currentValue != null) {
                    targetCollection = cloneValue(currentValue);
                }

                // No cloned instance so if the modelType happens to represent a
                // concrete type (probably not the norm) try to reflect a
                // no-argument constructor and invoke if available.
                if (targetCollection == null) {
                    //noinspection unchecked
                    targetCollection =
                          createCollection(currentValue, modelType);
                }

                // No suitable instance to work with, make our best guess
                // based on the type.
                if (targetCollection == null) {
                    //noinspection unchecked
                    targetCollection = bestGuess(modelType, values.length);
                }
            }

            //noinspection ManualArrayToCollectionCopy
            for (Object v : values) {
                //noinspection unchecked
                targetCollection.add(v);
            }

            return targetCollection;
        } else if (Object.class.equals(modelType)) {
            return convertSelectManyValues(context,
                                           uiSelectMany,
                                           Object[].class,
                                           newValues);
        } else {
            throw new FacesException("Target model Type is no a Collection or Array");
        }
        
    }




    protected Object convertSelectManyValues(FacesContext context,
                                             UISelectMany uiSelectMany,
                                             Class arrayClass,
                                             String[] newValues)
          throws ConverterException {

        Object result;
        Converter converter;
        int len = (null != newValues ? newValues.length : 0);

        Class 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;
                }
                StringBuffer valueStr = new StringBuffer();
                for (int i = 0; i < len; i++) {
                    if (i == 0) {
                        valueStr.append(newValues[i]);
                    } else {
                        valueStr.append(' ').append(newValues[i]);
                    }
                }
                Object[] params = {
                      valueStr.toString(),
                      "null Converter"
                };

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

        assert(null != result);
        if (elementType.isPrimitive()) {
            for (int i = 0; i < len; i++) {
                if (elementType.equals(Boolean.TYPE)) {
                    Array.setBoolean(result, i,
                                     ((Boolean) converter.getAsObject(context,
                                                                      uiSelectMany,
                                                                      newValues[i])));
                } else if (elementType.equals(Byte.TYPE)) {
                    Array.setByte(result, i,
                                  ((Byte) converter.getAsObject(context,
                                                                uiSelectMany,
                                                                newValues[i])));
                } else if (elementType.equals(Double.TYPE)) {
                    Array.setDouble(result, i,
                                    ((Double) converter.getAsObject(context,
                                                                    uiSelectMany,
                                                                    newValues[i])));
                } else if (elementType.equals(Float.TYPE)) {
                    Array.setFloat(result, i,
                                   ((Float) converter.getAsObject(context,
                                                                  uiSelectMany,
                                                                  newValues[i])));
                } else if (elementType.equals(Integer.TYPE)) {
                    Array.setInt(result, i,
                                 ((Integer) converter.getAsObject(context,
                                                                  uiSelectMany,
                                                                  newValues[i])));
                } else if (elementType.equals(Character.TYPE)) {
                    Array.setChar(result, i,
                                  ((Character) converter.getAsObject(context,
                                                                     uiSelectMany,
                                                                     newValues[i])));
                } else if (elementType.equals(Short.TYPE)) {
                    Array.setShort(result, i,
                                   ((Short) converter.getAsObject(context,
                                                                  uiSelectMany,
                                                                  newValues[i])));
                } else if (elementType.equals(Long.TYPE)) {
                    Array.setLong(result, i,
                                  ((Long) converter.getAsObject(context,
                                                                uiSelectMany,
                                                                newValues[i])));
                }
            }
        } else {
            for (int i = 0; i < len; i++) {
                if (logger.isLoggable(Level.FINE)) {
                    Object converted = converter.getAsObject(context,
                                                             uiSelectMany,
                                                             newValues[i]);
                    logger.fine("String value: " + newValues[i] +
                                " converts to : " + converted);
                }
                Array.set(result, i, converter.getAsObject(context,
                                                           uiSelectMany,
                                                           newValues[i]));
            }
        }
        return result;

    }


    protected boolean renderOption(FacesContext context,
                                   UIComponent component,
                                   UIComponent selectComponent,
                                   Converter converter,
                                   SelectItem curItem,
                                   Object currentSelections,
                                   Object[] submittedValues,
                                   OptionComponentInfo optionInfo) throws IOException {

        Object valuesArray;
        Object itemValue;
        String valueString = getFormattedValue(context, component,
                                               curItem.getValue(), converter);
        boolean containsValue;
        if (submittedValues != null) {
            containsValue = containsaValue(submittedValues);
            if (containsValue) {
                valuesArray = submittedValues;
                itemValue = valueString;
            } else {
                valuesArray = currentSelections;
                itemValue = curItem.getValue();
            }
        } else {
            valuesArray = currentSelections;
            itemValue = curItem.getValue();
        }

        boolean isSelected = isSelected(context, component, itemValue, valuesArray, converter);
        if (optionInfo.isHideNoSelection()
                && curItem.isNoSelectionOption()
                && currentSelections != null
                && !isSelected) {
            return false;
        }

        ResponseWriter writer = context.getResponseWriter();
        assert (writer != null);
        writer.writeText("\t", component, null);
        writer.startElement("option", (null != selectComponent) ? selectComponent : component);
        writer.writeAttribute("value", valueString, "value");

        if (isSelected) {
            writer.writeAttribute("selected", true, "selected");
        }

        // if the component is disabled, "disabled" attribute would be rendered
        // on "select" tag, so don't render "disabled" on every option.
        if ((!optionInfo.isDisabled()) && curItem.isDisabled()) {
            writer.writeAttribute("disabled", true, "disabled");
        }

        String labelClass;
        if (optionInfo.isDisabled() || curItem.isDisabled()) {
            labelClass = optionInfo.getDisabledClass();
        } else {
            labelClass = optionInfo.getEnabledClass();
        }
        if (labelClass != null) {
            writer.writeAttribute("class", labelClass, "labelClass");
        }

        if (curItem.isEscape()) {
            String label = curItem.getLabel();
            if (label == null) {
                label = valueString;
            }
            writer.writeText(label, component, "label");
        } else {
            writer.write(curItem.getLabel());
        }
        writer.endElement("option");
        writer.writeText("\n", component, null);
        return true;
    }


    protected void writeDefaultSize(ResponseWriter writer, int itemCount)
          throws IOException {

        // if size is not specified default to 1.
        writer.writeAttribute("size", "1", "size");

    }


    protected boolean containsaValue(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 && !(value.equals(RIConstants.NO_VALUE))) {
                    return true;
                }
            }
        }
        return false;

    }


    protected Object getCurrentSelectedValues(UIComponent component) {

        if (component instanceof UISelectMany) {
            UISelectMany select = (UISelectMany) component;
            Object value = select.getValue();
            if (value == null) {
                return null;
            } else if (value instanceof Collection) {
                return ((Collection) value).toArray();
            } else if (value.getClass().isArray()) {
                if (Array.getLength(value) == 0) {
                    return null;
                }
            } else if (!value.getClass().isArray()) {
                logger.warning(
                    "The UISelectMany value should be an array or a collection type, the actual type is " +
                    value.getClass().getName());
            }

            return value;
        }

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

    }


    // To derive a selectOne type component from this, override
    // these methods.
    protected String getMultipleText(UIComponent component) {

        if (component instanceof UISelectMany) {
            return " multiple ";
        }
        return "";

    }

    protected Object[] getSubmittedSelectedValues(UIComponent component) {

        if (component instanceof UISelectMany) {
            UISelectMany select = (UISelectMany) component;
            return (Object[]) select.getSubmittedValue();
        }

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

    }


    protected boolean isSelected(FacesContext context,
                                 UIComponent component,
                                 Object itemValue,
                                 Object valueArray,
                                 Converter converter) {

        if (itemValue == null && valueArray == null) {
            return true;
        }
        if (null != valueArray) {
            if (!valueArray.getClass().isArray()) {
                logger.warning("valueArray is not an array, the actual type is " +
                    valueArray.getClass());
                return valueArray.equals(itemValue);
            }
            int len = Array.getLength(valueArray);
            for (int i = 0; i < len; i++) {
                Object value = Array.get(valueArray, i);
                if (value == null && itemValue == null) {
                    return true;
                } else {
                    if ((value == null) ^ (itemValue == null)) {
                        continue;
                    }
                    Object compareValue;
                    if (converter == null) {
                        compareValue = coerceToModelType(context,
                                                        itemValue,
                                                        value.getClass());
                    } else {
                        compareValue = itemValue;
                        if (compareValue instanceof String && !(value instanceof String)) {
                            // type mismatch between the time and the value we're
                            // comparing.  Invoke the Converter.
                            compareValue = converter.getAsObject(context,
                                                                component,
                                                                (String) compareValue);
                        }
                    }

                    if (value.equals(compareValue)) {
                        return (true);
                    }
                }
            }
        }
        return false;

    }


    protected int renderOptions(FacesContext context,
                                UIComponent component,
                                SelectItemsIterator items)
    throws IOException {

        ResponseWriter writer = context.getResponseWriter();
        assert(writer != null);

        Converter converter = null;
        if(component instanceof ValueHolder) {
            converter = ((ValueHolder)component).getConverter();
        }
        int count = 0;
        Object currentSelections = getCurrentSelectedValues(component);
        Object[] submittedValues = getSubmittedSelectedValues(component);
        Map attributes = component.getAttributes();
        boolean componentDisabled = Util.componentIsDisabled(component);

        OptionComponentInfo optionInfo =
              new OptionComponentInfo((String) attributes.get("disabledClass"),
                                      (String) attributes.get("enabledClass"),
                                      componentDisabled,
                                      isHideNoSelection(component));
        RequestStateManager.set(context,
                                RequestStateManager.TARGET_COMPONENT_ATTRIBUTE_NAME,
                                component);
        while (items.hasNext()) {
            SelectItem item = items.next();
            UIComponent selectComponent = items.currentSelectComponent();

            if (item instanceof SelectItemGroup) {
                // render OPTGROUP
                writer.startElement("optgroup", (null != selectComponent) ? selectComponent : component);
                writer.writeAttribute("label", item.getLabel(), "label");

                // if the component is disabled, "disabled" attribute would be rendered
                // on "select" tag, so don't render "disabled" on every option.
                if ((!componentDisabled) && item.isDisabled()) {
                    writer.writeAttribute("disabled", true, "disabled");
                }
                count++;
                // render options of this group.
                SelectItem[] itemsArray =
                      ((SelectItemGroup) item).getSelectItems();
                for (int i = 0; i < itemsArray.length; ++i) {
                    if (renderOption(context,
                                     component,
                                     selectComponent,
                                     converter,
                                     itemsArray[i],
                                     currentSelections,
                                     submittedValues,
                                     optionInfo)) {
                        count++;
                    }
                }
                writer.endElement("optgroup");
            } else {
                if (renderOption(context,
                                 component,
                                 selectComponent,
                                 converter,
                                 item,
                                 currentSelections,
                                 submittedValues,
                                 optionInfo)) {
                    count ++;
                }
            }
        }

        return count;

    }


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

        ResponseWriter writer = context.getResponseWriter();
        assert(writer != null);

        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "Rendering 'select'");
        }
        writer.startElement("select", component);
        writeIdAttributeIfNecessary(context, writer, component);
        writer.writeAttribute("name", component.getClientId(context),
                              "clientId");
        // render styleClass attribute if present.
        String styleClass;
        if (null !=
            (styleClass =
                  (String) component.getAttributes().get("styleClass"))) {
            writer.writeAttribute("class", styleClass, "styleClass");
        }
        if (!getMultipleText(component).equals("")) {
            writer.writeAttribute("multiple", true, "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
        SelectItemsIterator items = RenderKitUtils.getSelectItems(context, component);

        // render the options to a buffer now so that we can determine
        // the size
        FastStringWriter bufferedWriter = new FastStringWriter(128);
        context.setResponseWriter(writer.cloneWithWriter(bufferedWriter));
        int count = renderOptions(context, component, items);
        context.setResponseWriter(writer);
        // If "size" is *not* set explicitly, we have to default it correctly
        Integer size = (Integer) component.getAttributes().get("size");
        if (size == null || size == Integer.MIN_VALUE) {
            size = count;
        }
        writeDefaultSize(writer, size);

        RenderKitUtils.renderPassThruAttributes(context,
                                                writer,
                                                component,
                                                ATTRIBUTES,
                                                getNonOnChangeBehaviors(component));
        RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer,
                                                         component);

        RenderKitUtils.renderOnchange(context, component, false);

        // Now, write the buffered option content
        writer.write(bufferedWriter.toString());
        
        writer.endElement("select");

    }

    protected Object coerceToModelType(FacesContext ctx,
                                       Object value,
                                       Class itemValueType) {

        Object newValue;
        try {
            ExpressionFactory ef = ctx.getApplication().getExpressionFactory();
            newValue = ef.coerceToType(value, itemValueType);
        } catch (ELException ele) {
            newValue = value;
        } catch (IllegalArgumentException iae) {
            // If coerceToType fails, per the docs it should throw
            // an ELException, however, GF 9.0 and 9.0u1 will throw
            // an IllegalArgumentException instead (see GF issue 1527).
            newValue = value;
        }

        return newValue;

    }


    /**
     * @param collection a Collection instance
     *
     * @return a new Collection instance or null if the instance
     *         cannot be created
     */
    protected Collection createCollection(Collection collection,
                                          Class fallBackType) {

        Class lookupClass =
              ((collection != null) ? collection.getClass() : fallBackType);

        if (!lookupClass.isInterface()
             && !Modifier.isAbstract(lookupClass.getModifiers())) {
            try {
                return lookupClass.newInstance();
            } catch (Exception e) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE,
                               "Unable to create new Collection instance for type "
                               + lookupClass.getName(),
                               e);
                }
            }
        }

        return null;

    }


    /**
     * 

* Utility method to invoke the the clone method on the provided * value. *

* * @param value the value to clone * @return the result of invoking clone() or null * if the value could not be cloned or does not implement the * {@link Cloneable} interface */ protected Collection cloneValue(Object value) { if (value instanceof Cloneable) { // even though Clonable marks an instance of a Class as being // safe to call .clone(), .clone() by default is protected. // The Collection classes that do implement Clonable do so at variable // locations within the class hierarchy, so we're stuck having to // use reflection. Method clone = ReflectionUtils.lookupMethod(value.getClass(), "clone"); if (clone != null) { try { Collection c = (Collection) clone.invoke(value); c.clear(); return c; } catch (Exception e) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "Unable to clone collection type: {0}", value.getClass().getName()); logger.log(Level.SEVERE, e.toString(), e); } } } else { // no public clone method if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Type {0} implements Cloneable, but has no public clone method.", value.getClass().getName()); } } } return null; } /** * @param type the target model type * @param initialSize the initial size of the Collection * @return a Collection instance that best matches * type */ protected Collection bestGuess(Class type, int initialSize) { if (SortedSet.class.isAssignableFrom(type)) { return new TreeSet(); } else if (Queue.class.isAssignableFrom(type)) { return new LinkedList(); } else if (Set.class.isAssignableFrom(type)) { return new HashSet(initialSize); } else { // this covers the where type is List or Collection return new ArrayList(initialSize); } } /** *

* Create a collection from the provided hint. * @param collectionTypeHint the Collection type as either a String or Class * @return a new Collection instance */ protected Collection createCollectionFromHint(Object collectionTypeHint) { Class collectionType; if (collectionTypeHint instanceof Class) { //noinspection unchecked collectionType = (Class) collectionTypeHint; } else if (collectionTypeHint instanceof String) { try { //noinspection unchecked collectionType = Util.loadClass((String) collectionTypeHint, this); } catch (ClassNotFoundException cnfe) { throw new FacesException(cnfe); } } else { // RELEASE_PENDING (i18n) throw new FacesException( "'collectionType' should resolve to type String or Class. Found: " + collectionTypeHint.getClass().getName()); } Collection c = createCollection(null, collectionType); if (c == null) { // RELEASE_PENDING (i18n) throw new FacesException("Unable to create collection type " + collectionType); } return c; } protected boolean isHideNoSelection(UIComponent component) { Object result = component.getAttributes().get("hideNoSelectionOption"); return ((result != null) ? (Boolean) result : false); } } // end of class MenuRenderer