/*
 * Copyright 2004-2014 ICEsoft Technologies Canada Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS
 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

package com.icesoft.faces.renderkit.dom_html_basic;

import com.icesoft.faces.component.AttributeConstants;
import com.icesoft.faces.context.DOMContext;
import com.icesoft.faces.context.effects.JavascriptContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import javax.faces.component.*;
import javax.faces.component.html.*;
import javax.faces.context.FacesContext;
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.*;
import java.util.logging.Logger;

public class MenuRenderer extends DomBasicInputRenderer {

    private static final Logger log = Logger.getLogger(MenuRenderer.class.getName());
    private static final String[] selectOneMenuPassThruAttributes = AttributeConstants.getAttributes(AttributeConstants.H_SELECTONEMENU);
    private static final String[] selectManyMenuPassThruAttributes = AttributeConstants.getAttributes(AttributeConstants.H_SELECTMANYMENU);
    private static final String[] selectOneListboxPassThruAttributes = AttributeConstants.getAttributes(AttributeConstants.H_SELECTONELISTBOX);
    private static final String[] selectManyListboxPassThruAttributes = AttributeConstants.getAttributes(AttributeConstants.H_SELECTMANYLISTBOX);

    public void decode(FacesContext facesContext, UIComponent uiComponent) {
        validateParameters(facesContext, uiComponent, null);
        if (isStatic(uiComponent)) {
            return;
        }
        String clientId = uiComponent.getClientId(facesContext);
        if (uiComponent instanceof UISelectMany) {
            Map requestParameterValuesMap = facesContext.getExternalContext()
                    .getRequestParameterValuesMap();
            if (requestParameterValuesMap.containsKey(clientId)) {
                String[] decodedValue =
                        (String[]) requestParameterValuesMap.get(clientId);
                setSubmittedValue(uiComponent, decodedValue);
            } else {
                // This represents a deselected control
                setSubmittedValue(uiComponent, new String[0]);
            }
        } else if (uiComponent instanceof UISelectOne) {
            Map requestParameterValuesMap = facesContext.getExternalContext()
                    .getRequestParameterValuesMap();
            String decodedValue = null;
            if ((requestParameterValuesMap != null) && (requestParameterValuesMap.containsKey(clientId))) {
                decodedValue =
                        ((String[]) requestParameterValuesMap.get(clientId))[0];
            } else {
                //none of the option has been selected
                //set it to a blank string, not to null
                decodedValue = "";
            }
            ((UISelectOne) uiComponent).setSubmittedValue(decodedValue);

        }
        return;
    }

    public void encodeBegin(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {
        validateParameters(facesContext, uiComponent, null);
    }

    public void encodeChildren(FacesContext facesContext,
                               UIComponent uiComponent) {
        validateParameters(facesContext, uiComponent, null);
    }

    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {
        validateParameters(facesContext, uiComponent, null);

        renderSelect(facesContext, uiComponent);
        JavascriptContext.fireEffect(uiComponent, facesContext);
    }

    public Object getConvertedValue(FacesContext facesContext,
                                    UIComponent uiComponent,
                                    Object newSubmittedValue)
            throws ConverterException {
        if (uiComponent instanceof UISelectOne) {
            if (newSubmittedValue == null || "".equals(newSubmittedValue)) {
                return null;
            } else {
                return super.getConvertedValue(facesContext,
                        (UISelectOne) uiComponent,
                        newSubmittedValue);
            }
        } else {
            return convertSelectValue(facesContext,
                    ((UISelectMany) uiComponent),
                    (String[]) newSubmittedValue);
        }
    }

    public Object convertSelectValue(FacesContext facesContext,
                                     UIComponent uiComponent,
                                     Object newSubmittedValue)
            throws ConverterException {
        if (uiComponent instanceof UISelectOne) {
            if (newSubmittedValue == null || "".equals(newSubmittedValue)) {
                return null;
            } else {
                return super.getConvertedValue(facesContext,
                        (UISelectOne) uiComponent,
                        newSubmittedValue);
            }
        } else {
            return convertSelectValue(facesContext,
                    ((UISelectMany) uiComponent),
                    (String[]) newSubmittedValue);
        }
    }

    public Object convertSelectValue(FacesContext facesContext,
                                     UISelectMany uiSelectMany,
                                     String[] newSubmittedValues)
            throws ConverterException {

        ValueBinding valueBinding = uiSelectMany.getValueBinding("value");
        if (valueBinding == null) {
            Class componentType = (new Object[1]).getClass().getComponentType();
            return convertArray(facesContext, uiSelectMany, componentType,
                    newSubmittedValues);
        }
        Class valueBindingClass = valueBinding.getType(facesContext);
        if (valueBindingClass == null) {
            throw new ConverterException("Inconvertible type in value binding");
        }
        if (List.class.isAssignableFrom(valueBindingClass)) {
            Converter converter = uiSelectMany.getConverter();
            if (converter == null) {
                // Determine if there is a default converter for the class
                converter = getConverterForClass(valueBindingClass);
            }

            ArrayList submittedValuesAsList = new ArrayList(
                    newSubmittedValues.length);
            for (int index = 0; index < newSubmittedValues.length; index++) {
                Object convertedValue = newSubmittedValues[index];
                if (converter != null) {
                    convertedValue = converter.getAsObject(
                            facesContext, uiSelectMany, newSubmittedValues[index]);
                }
                submittedValuesAsList.add(convertedValue);
            }
            return submittedValuesAsList;
        }
        if (valueBindingClass.isArray()) {
            Class componentType = valueBindingClass.getComponentType();
            return convertArray(facesContext, uiSelectMany, componentType,
                    newSubmittedValues);
        }
        throw new ConverterException(
                "Non-list and Non-array values are inconvertible");
    }

    protected Object convertArray(FacesContext facesContext,
                                  UISelectMany uiSelectMany,
                                  Class componentType,
                                  String[] newSubmittedValues)
            throws ConverterException {

        // component type of String means no conversion is necessary
        if (componentType.equals(String.class)) {
            return newSubmittedValues;
        }

        // if newSubmittedValue is null return zero-length array
        if (newSubmittedValues == null) {
            return Array.newInstance(componentType, 0);
        }

        // create the array with specified component length
        int numberOfValues = newSubmittedValues.length;
        Object convertedValues = Array.newInstance(componentType,
                numberOfValues);

        // Determine if a converter is explicitly registered with the component
        Converter converter = uiSelectMany.getConverter();
        if (converter == null) {
            // Determine if there is a default converter for the class
            converter = getConverterForClass(componentType);
        }
        if (converter == null) {
            // we don't need to convert base Object types
            if (componentType.equals(Object.class)) {
                return newSubmittedValues;
            } else {
                throw new ConverterException("Converter is null");
            }
        }

        for (int index = 0; index < numberOfValues; index++) {

            // convert the next element
            Object nextConvertedElement = converter.getAsObject(facesContext,
                    uiSelectMany,
                    newSubmittedValues[index]);

            if (!componentType.isPrimitive()) {
                Array.set(convertedValues, index, nextConvertedElement);
            } else if (componentType.equals(Boolean.TYPE)) {

                Array.setBoolean(convertedValues, index,
                        ((Boolean) nextConvertedElement).booleanValue());

            } else if (componentType.equals(Integer.TYPE)) {

                Array.setInt(convertedValues, index,
                        ((Integer) nextConvertedElement).intValue());

            } else if (componentType.equals(Long.TYPE)) {

                Array.setLong(convertedValues, index,
                        ((Long) nextConvertedElement).longValue());

            } else if (componentType.equals(Short.TYPE)) {

                Array.setShort(convertedValues, index,
                        ((Short) nextConvertedElement).shortValue());

            } else if (componentType.equals(Byte.TYPE)) {

                Array.setByte(convertedValues, index,
                        ((Byte) nextConvertedElement).byteValue());

            } else if (componentType.equals(Float.TYPE)) {

                Array.setFloat(convertedValues, index,
                        ((Float) nextConvertedElement).floatValue());

            } else if (componentType.equals(Double.TYPE)) {

                Array.setDouble(convertedValues, index,
                        ((Double) nextConvertedElement).doubleValue());

            } else if (componentType.equals(Character.TYPE)) {

                Array.setChar(convertedValues, index,
                        ((Character) nextConvertedElement).charValue());

            }
        }
        return convertedValues;
    }

    protected void renderOption(FacesContext facesContext,
                                UIComponent uiComponent,
                                SelectItem selectItem, Element optionGroup,
                                Object[] submittedValues, Object selectedValues)
            throws IOException {

        DOMContext domContext =
                DOMContext.getDOMContext(facesContext, uiComponent);

        Element select = (Element) domContext.getRootNode();
        Element option = domContext.createElement("option");

        if (optionGroup == null) {
            select.appendChild(option);
        } else {
            optionGroup.appendChild(option);
        }

        String valueString = formatComponentValue(facesContext, uiComponent,
                selectItem.getValue());
        option.setAttribute("value", valueString);

        boolean selected = isValueSelected(facesContext, selectItem, uiComponent,
                submittedValues, selectedValues);
        if (uiComponent instanceof HtmlSelectOneMenu || uiComponent instanceof HtmlSelectOneListbox) {
            if (submittedValues == null && selectedValues == null &&
                    (selectItem.getValue() == "" || selectItem.getValue() == null))
                selected = true;
        }

        if (selected) {
            option.setAttribute("selected", "selected");
        }
        if (selectItem.isDisabled()) {
            option.setAttribute("disabled", "disabled");
        }

        Document doc = domContext.getDocument();
        String label = selectItem.getLabel();
        Text labelNode;
        if (selectItem.isEscape()) {
            labelNode = domContext.createTextNode(label == null ? valueString : label);
        } else {
            labelNode = domContext.createTextNodeUnescaped(label == null ? valueString : label);
        }
        option.appendChild(labelNode);
    }

    protected boolean isValueSelected(
            FacesContext facesContext,
            SelectItem selectItem,
            UIComponent uiComponent,
            Object[] submittedValues,
            Object selectedValues) {
        if (submittedValues != null) {
            String valueString = formatComponentValue(facesContext, uiComponent, selectItem.getValue());
            return isSelected(valueString, submittedValues);
        }
        return isSelected(selectItem.getValue(), selectedValues, facesContext, uiComponent);
    }

    void renderSelect(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {
        HashSet excludes = new HashSet();
        // setup
        DOMContext domContext =
                DOMContext.attachDOMContext(facesContext, uiComponent);
        if (!domContext.isInitialized()) {
            Element root = domContext.createElement("select");
            domContext.setRootNode(root);
            setRootElementId(facesContext, root, uiComponent);
            root.setAttribute("name", uiComponent.getClientId(facesContext));
            // render styleClass attribute if present.
            String styleClass = null;
            if (null != (styleClass = (String) uiComponent.getAttributes().get(
                    "styleClass"))) {
                root.setAttribute("class", styleClass);
            }
            if (!getMultipleText(uiComponent).equals("")) {
                root.setAttribute("multiple", "multiple");
            }
        }
        Element root = (Element) domContext.getRootNode();

        // 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 = countSelectOptionsRecursive(facesContext, uiComponent);
        // If "size" is *not* set explicitly, we have to default it correctly
        Object size = uiComponent.getAttributes().get("size");
        if ((null == size)
                || ((size instanceof Integer) &&
                (((Integer) size).intValue() == Integer.MIN_VALUE ||
                        ((Integer) size).intValue() == 0))) {
            renderSizeAttribute(root, itemCount);
            excludes.add("size");
        } else {
            renderSizeAttribute(root, Integer.valueOf(size.toString()).intValue());
        }

        Object currentValue = null;
        if (null ==
                (currentValue = ((UIInput) uiComponent).getSubmittedValue())) {
            currentValue = "";
        }

        addJavaScript(facesContext, uiComponent, root, currentValue.toString(),
                excludes);

        if (uiComponent instanceof HtmlSelectOneMenu) {
            PassThruAttributeRenderer.renderHtmlAttributes(facesContext, uiComponent, selectOneMenuPassThruAttributes);
            addJavaScriptOverride(facesContext, uiComponent, root, currentValue.toString(), excludes);
        } else if (uiComponent instanceof HtmlSelectManyMenu) {
            PassThruAttributeRenderer.renderHtmlAttributes(facesContext, uiComponent, selectManyMenuPassThruAttributes);
            addJavaScriptOverride(facesContext, uiComponent, root, currentValue.toString(), excludes);
        } else if (uiComponent instanceof HtmlSelectOneListbox) {
            PassThruAttributeRenderer.renderHtmlAttributes(facesContext, uiComponent, selectOneListboxPassThruAttributes);
			addJavaScriptOverride(facesContext, uiComponent, root, currentValue.toString(), excludes);
        } else if (uiComponent instanceof HtmlSelectManyListbox) {
            PassThruAttributeRenderer.renderHtmlAttributes(facesContext, uiComponent, selectManyListboxPassThruAttributes);
			addJavaScriptOverride(facesContext, uiComponent, root, currentValue.toString(), excludes);
        }
        String[] attributes = new String[]{HTML.DISABLED_ATTR, HTML.READONLY_ATTR};
        Object attribute;
        for (int i = 0; i < attributes.length; i++) {
            attribute = uiComponent.getAttributes().get(attributes[i]);
            if (attribute instanceof Boolean && ((Boolean) attribute).booleanValue()) {
                root.setAttribute(attributes[i], attributes[i]);
            }
        }
        excludes.clear();

        domContext.stepInto(uiComponent);
        renderOptions(facesContext, uiComponent);
        domContext.stepOver();
    }

    public String getEventType(UIComponent uiComponent) {
        if (uiComponent instanceof javax.faces.component.html.HtmlSelectOneListbox) {
            return "onchange";
        } else if (uiComponent instanceof javax.faces.component.html.HtmlSelectOneMenu) {
            return "onchange";
        } else if (uiComponent instanceof javax.faces.component.html.HtmlSelectManyListbox) {
            return "onchange";
        } else if (uiComponent instanceof javax.faces.component.html.HtmlSelectManyMenu) {
            return "onchange";
        } else if (uiComponent instanceof HtmlSelectManyCheckbox) {
            return "onclick";
        }
        return "";
    }

    int countSelectOptionsRecursive(FacesContext facesContext,
                                    UIComponent uiComponent) {
        int counter = 0;
        Iterator selectItems = getSelectItems(uiComponent);
        while (selectItems.hasNext()) {
            counter++;
            SelectItem nextSelectItem = (SelectItem) selectItems.next();
            if (nextSelectItem instanceof SelectItemGroup) {
                counter += ((SelectItemGroup) nextSelectItem)
                        .getSelectItems().length;
            }
        }
        return counter;
    }

    void renderOptions(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {

        DOMContext domContext =
                DOMContext.getDOMContext(facesContext, uiComponent);

        Element rootSelectElement = (Element) domContext.getRootNode();
        DOMContext.removeChildrenByTagName(rootSelectElement, "option");
        DOMContext.removeChildrenByTagName(rootSelectElement, "optgroup");

        Iterator selectItems = getSelectItems(uiComponent);
        Object[] submittedValues = null;
        Object selectedValues = null;
        if (selectItems.hasNext()) {
            submittedValues = getSubmittedSelectedValues(uiComponent);
            if (submittedValues == null) {
                selectedValues = getCurrentSelectedValues(uiComponent);
            }
        }
        while (selectItems.hasNext()) {
            SelectItem nextSelectItem = (SelectItem) selectItems.next();
            if (nextSelectItem instanceof SelectItemGroup) {
                Element optGroup = domContext.createElement("optgroup");
                rootSelectElement.appendChild(optGroup);
                optGroup.setAttribute("label", nextSelectItem.getLabel());
                domContext.setCursorParent(optGroup);
                SelectItem[] selectItemsArray =
                        ((SelectItemGroup) nextSelectItem).getSelectItems();
                for (int i = 0; i < selectItemsArray.length; ++i) {
                    renderOption(facesContext, uiComponent, selectItemsArray[i],
                            optGroup, submittedValues, selectedValues);
                }
            } else {
                renderOption(facesContext, uiComponent, nextSelectItem, null,
                        submittedValues, selectedValues);
            }
        }
    }

    boolean isSelected(Object sentinel, Object selectedValues, FacesContext facesContext, UIComponent uiComponent) {
        boolean isSelected = false;
        if (selectedValues == null || sentinel == null) {
            return isSelected;
        }
        String formattedSelectedValue;
        String formattedSentinel = formatComponentValue(facesContext, uiComponent, sentinel);
        int length = Array.getLength(selectedValues);
        for (int index = 0; index < length; index++) {
            Object nextSelectedValue = Array.get(selectedValues, index);
            formattedSelectedValue = formatComponentValue(facesContext, uiComponent, nextSelectedValue);
            if (nextSelectedValue == null && sentinel == null) {
                isSelected = true;
                break;
            } else if (nextSelectedValue != null && nextSelectedValue.equals(sentinel)) {
                isSelected = true;
                break;
            } else if (sentinel instanceof String) {
                if (isConversionMatched(sentinel.toString(), nextSelectedValue)) {
                    isSelected = true;
                    break;
                }
                if (formattedSelectedValue.equals(sentinel)) {
                    isSelected = true;
                    break;
                }
            } else if (formattedSelectedValue != null && formattedSelectedValue.equals(formattedSentinel)) {
                isSelected = true;
                break;
            }
        }
        return isSelected;
    }

    boolean isSelected(Object sentinelValue, Object[] selectedValues) {
        boolean valueIsSelected = false;
        if (selectedValues != null) {
            Iterator selectedValuesIterator =
                    Arrays.asList(selectedValues).iterator();
            while (selectedValuesIterator.hasNext()) {
                if (selectedValuesIterator.next().equals(sentinelValue)) {
                    valueIsSelected = true;
                    break;
                }
            }
        }
        return valueIsSelected;
    }

    /**
     * Render "1" as the value of the size attribute
     *
     * @param targetElement
     * @param size
     * @throws IOException
     */
    protected void renderSizeAttribute(Element targetElement, int size)
            throws IOException {
        targetElement.setAttribute("size", "1");
    }

    String getSelectedTextString() {
        return " selected";
    }

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

    protected Object[] getSubmittedSelectedValues(UIComponent uiComponent) {
        if (uiComponent instanceof UISelectMany) {
            UISelectMany uiSelectMany = (UISelectMany) uiComponent;
            return (Object[]) uiSelectMany.getSubmittedValue();
        }
        if (uiComponent instanceof UISelectOne) {
            UISelectOne uiSelectOne = (UISelectOne) uiComponent;
            Object submittedValue = uiSelectOne.getSubmittedValue();
            if (submittedValue != null) {
                return new Object[]{submittedValue};
            }
        }
        return null;
    }


    protected Object getCurrentSelectedValues(UIComponent uiComponent) {
        Object currentSelectedValues = null;
        if (uiComponent instanceof UISelectMany) {
            UISelectMany uiSelectMany = (UISelectMany) uiComponent;
            currentSelectedValues = uiSelectMany.getValue();
            if (currentSelectedValues instanceof List) {
                return ((List) currentSelectedValues).toArray();
            }
            return currentSelectedValues;
        }
        if (uiComponent instanceof UISelectOne) {
            UISelectOne uiSelectOne = (UISelectOne) uiComponent;
            currentSelectedValues = uiSelectOne.getValue();
            if (currentSelectedValues != null) {
                return new Object[]{currentSelectedValues};
            }
        }
        return null;
    }

    protected Iterator getSelectItems(UIComponent uiComponent) {

        List selectItems = new ArrayList();
        if (uiComponent.getChildCount() == 0) return selectItems.iterator();
        Iterator children = uiComponent.getChildren().iterator();

        while (children.hasNext()) {
            UIComponent nextSelectItemChild = (UIComponent) children.next();
            if (nextSelectItemChild instanceof UISelectItem) {
                Object selectItemValue =
                        ((UISelectItem) nextSelectItemChild).getValue();
                if (selectItemValue != null &&
                        selectItemValue instanceof SelectItem) {
                    selectItems.add(selectItemValue);
                } else {
                    //If user defines only one member, either itemValue or itemLabel
                    //The default implementation throws a null pointer exception.
                    //So here we are identifying, if either itemValue or itemLabel is found,
                    //Assigned its value to the other member
                    assignDataIfNull(nextSelectItemChild);
                    selectItems.add(
                            new SelectItem(
                                    ((UISelectItem) nextSelectItemChild).getItemValue(),
                                    ((UISelectItem) nextSelectItemChild).getItemLabel(),
                                    ((UISelectItem) nextSelectItemChild).getItemDescription(),
                                    ((UISelectItem) nextSelectItemChild).isItemDisabled()));
                }
            } else if (nextSelectItemChild instanceof UISelectItems) {
                Object selectItemsValue =
                        ((UISelectItems) nextSelectItemChild).getValue();

                if (selectItemsValue != null) {
                    Object itemLabelEscaped = nextSelectItemChild.getAttributes().get("itemLabelEscaped");
                    boolean isItemLabelEscaped = true;
                    if (itemLabelEscaped != null) {
                        isItemLabelEscaped = Boolean.valueOf(itemLabelEscaped.toString()).booleanValue();
                    }
                    SelectItem selectItem;
                    if (selectItemsValue instanceof SelectItem) {
                        if (itemLabelEscaped != null) {
                            selectItem = (SelectItem) selectItemsValue;
                            selectItem.setEscape(isItemLabelEscaped);
                        }
                        selectItems.add(selectItemsValue);
                    } else if (selectItemsValue instanceof Collection) {
                        Iterator selectItemsIterator =
                                ((Collection) selectItemsValue).iterator();
                        while (selectItemsIterator.hasNext()) {
                            Object item = selectItemsIterator.next();
                            if (item instanceof SelectItem) {
                                if (itemLabelEscaped != null) {
                                    selectItem = (SelectItem) item;
                                    selectItem.setEscape(isItemLabelEscaped);
                                }
                                selectItems.add(item);
                            } else {

                                if (nextSelectItemChild.getAttributes().containsKey("var")) {
                                    String var = nextSelectItemChild.getAttributes().get("var").toString();
                                    Map<String, Object> map = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
                                    Object oldValue = map.put(var, item);

                                    String itemLabel = String.valueOf(nextSelectItemChild.getAttributes().get("itemLabel"));
                                    Object itemValue = nextSelectItemChild.getAttributes().get("itemValue");
                                    selectItem = new SelectItem((itemValue != null) ? itemValue : item, itemLabel);
                                    selectItem.setEscape(isItemLabelEscaped);
                                    selectItems.add(selectItem);
                                    continue;
                                }

                                selectItem = new SelectItem(item);
                                selectItem.setEscape(isItemLabelEscaped);
                                selectItems.add(selectItem);
                            }
                        }
                    } else if (selectItemsValue instanceof SelectItem[]) {
                        SelectItem selectItemArray[] =
                                (SelectItem[]) selectItemsValue;
                        for (int i = 0; i < selectItemArray.length; i++) {
                            if (itemLabelEscaped != null) {
                                selectItemArray[i].setEscape(isItemLabelEscaped);
                            }
                            selectItems.add(selectItemArray[i]);
                        }
                    } else if (selectItemsValue instanceof Object[]) {
                        Map<String, Object> map = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
                        for (Object item : (Object[]) selectItemsValue) {
                            if (nextSelectItemChild.getAttributes().containsKey("var")) {
                                String var = nextSelectItemChild.getAttributes().get("var").toString();
                                Object oldValue = map.put(var, item);
                                String itemLabel = String.valueOf(nextSelectItemChild.getAttributes().get("itemLabel"));
                                Object itemValue = nextSelectItemChild.getAttributes().get("itemValue");
                                selectItem = new SelectItem((itemValue != null) ? itemValue : item, itemLabel);
                                selectItem.setEscape(isItemLabelEscaped);
                                selectItems.add(selectItem);
                            } else {
                                selectItem = new SelectItem(item);
                                selectItem.setEscape(isItemLabelEscaped);
                                selectItems.add(selectItem);
                            }
                        }
                    } else if (selectItemsValue instanceof Map) {
                        Iterator selectItemIterator =
                                ((Map) selectItemsValue).keySet().iterator();
                        while (selectItemIterator.hasNext()) {
                            Object nextKey = selectItemIterator.next();
                            if (nextKey != null) {
                                Object nextValue =
                                        ((Map) selectItemsValue).get(nextKey);
                                if (nextValue != null) {
                                    selectItem = new SelectItem(
                                            nextValue.toString(),
                                            nextKey.toString());
                                    selectItem.setEscape(isItemLabelEscaped);
                                    selectItems.add(
                                            selectItem);
                                }
                            }
                        }
                    } else if (selectItemsValue instanceof String[]) {
                        String stringItemArray[] = (String[]) selectItemsValue;
                        for (int i = 0; i < stringItemArray.length; i++) {
                            selectItem = new SelectItem(stringItemArray[i]);
                            selectItem.setEscape(isItemLabelEscaped);
                            selectItems.add(selectItem);
                        }
                    } else if (selectItemsValue instanceof Enum[]) {
                        Enum[] enumArray = ((Enum[]) selectItemsValue);
                        for (int i = 0; i < enumArray.length; i++) {
                            selectItem = new SelectItem(enumArray[i]);
                            selectItem.setEscape(isItemLabelEscaped);
                            selectItems.add(selectItem);
                        }
                    }
                }
            }
        }
        return selectItems.iterator();
    }

    private void assignDataIfNull(Object selectItem) {
        UISelectItem uiSelectItem = (UISelectItem) selectItem;
        if (uiSelectItem.getItemValue() == null) {
            if (uiSelectItem.getItemLabel() != null) {
                uiSelectItem.setItemValue(uiSelectItem.getItemLabel());
            }
        }
        if (uiSelectItem.getItemLabel() == null) {
            if (uiSelectItem.getItemValue() != null) {
                uiSelectItem
                        .setItemLabel(uiSelectItem.getItemValue().toString());
            }
        }
    }

    protected void addJavaScript(FacesContext facesContext,
                                 UIComponent uiComponent, Element root,
                                 String currentValue,
                                 Set excludes) {
    }

    protected void addJavaScriptOverride(FacesContext facesContext,
                                 UIComponent uiComponent, Element root,
                                 String currentValue,
                                 Set excludes) {
    }	

    private boolean isConversionMatched(String sentinel, Object selectedValue) {
        boolean match = false;
        if (sentinel.length() == 0) {
            if (selectedValue == null) {
                match = true;
            }
        } else if (selectedValue instanceof Long) {
            if (selectedValue.equals(Long.valueOf(sentinel))) {
                match = true;
            }
        } else if (selectedValue instanceof Byte) {
            if (selectedValue.equals(Byte.valueOf(sentinel))) {
                match = true;
            }
        } else if (selectedValue instanceof Integer) {
            if (selectedValue.equals(Integer.valueOf(sentinel))) {
                match = true;
            }
        } else if (selectedValue instanceof Short) {
            if (selectedValue.equals(Short.valueOf(sentinel))) {
                match = true;
            }
        } else if (selectedValue instanceof Double) {
            if (selectedValue.equals(Double.valueOf(sentinel))) {
                match = true;
            }
        } else if (selectedValue instanceof Float) {
            if (selectedValue.equals(Float.valueOf(sentinel))) {
                match = true;
            }
        } else if (selectedValue instanceof Boolean) {
            if (selectedValue.equals(Boolean.valueOf(sentinel))) {
                match = true;
            }
        }
        return match;
    }
}
