org.richfaces.renderkit.PickListRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.richfaces.renderkit.PickListRenderer.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.renderkit;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;

import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
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 org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptString;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.HeaderResourcesRendererBase;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.ajax4jsf.util.SelectUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.component.UIPickList;
import org.richfaces.component.util.HtmlUtil;

public class PickListRenderer extends HeaderResourcesRendererBase {

    private static Log logger = LogFactory.getLog(PickListRenderer.class);

    private static final String HIDDEN_SUFFIX = "valueKeeper";

    private static final String MESSAGE_BUNDLE_NAME = PickListRenderer.class.getPackage().getName() + ".pickList";

    private static final String SHOW_LABELS_ATTRIBUTE_NAME = "showButtonsLabel";

    private static final OrderingComponentRendererBase.ControlsHelper[] SHUTTLE_HELPERS = PickListControlsHelper.HELPERS;

    protected static final class ListStateFlags {

        public ListStateFlags() {
            super();
        }

        protected boolean isSelectedList;

        protected boolean isAvailableList;
    }

    protected List<SelectItem> selectItemsForSelectedList(FacesContext facesContext, UIComponent uiComponent,
            List<SelectItem> selectItemList, List<Object> lookupList) {

        List<SelectItem> selectItemForSelectedValues = new ArrayList<SelectItem>();

        for (Object lookupItem : lookupList) {

            for (SelectItem selectItem : selectItemList) {

                if (selectItem instanceof SelectItemGroup) {

                    SelectItem[] items = ((SelectItemGroup) selectItem).getSelectItems();
                    for (int j = 0; j < items.length; j++) {
                        if (lookupItem.equals(items[j].getValue())) {
                            selectItemForSelectedValues.add(items[j]);
                        }
                    }

                } else {

                    if (lookupItem.equals(selectItem.getValue())) {
                        selectItemForSelectedValues.add(selectItem);
                    }

                }

            }

        }

        return selectItemForSelectedValues;
    }

    protected List<Object> getValuesList(UIPickList pickList) {

        List<Object> valuesList = new ArrayList<Object>();

        Object value = pickList.getValue();

        if (null == value || "".equals(value)) {
            return valuesList;
        }

        if (value.getClass().isArray()) {
            int len = Array.getLength(value);

            for (int i = 0; i < len; i++) {

                Object localValue = Array.get(value, i);
                if (localValue != null) {
                    valuesList.add(localValue);
                }

            }

            return valuesList;
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;

            for (Object item : list) {
                valuesList.add(item);
            }

            return valuesList;

        } else {
            throw new IllegalArgumentException("Error: value of UIPickList component is not of type Array or List");
        }

    }

    protected List<SelectItem> selectItemsForAvailableList(FacesContext facesContext, UIComponent uiComponent,
            List<SelectItem> selectItemList, List<SelectItem> selectItemsForSelectedList) {

        List<SelectItem> processItems = new ArrayList<SelectItem>();

        for (SelectItem item : selectItemList) {

            if (item instanceof SelectItemGroup) {

                SelectItem items[] = ((SelectItemGroup) item).getSelectItems();
                for (int i = 0; i < items.length; i++) {
                    processItems.add(items[i]);
                }

            } else {
                processItems.add(item);
            }

        }

        for (SelectItem selectItem : selectItemsForSelectedList) {
            processItems.remove(selectItem);
        }

        return processItems;

    }

    protected List<SelectItem> getSelectItemsList(FacesContext context, UIComponent component) {
        return SelectUtils.getSelectItems(context, component);
    }

    public PickListRenderer() {
        super();
    }

    public void decode(FacesContext context, UIComponent component) {

        UIPickList picklist = (UIPickList) component;
        if (!(picklist instanceof EditableValueHolder)) {
            throw new IllegalArgumentException(
                    "Component " + picklist.getClientId(context) + " is not an EditableValueHolder");
        }

        String hiddenClientId = picklist.getClientId(context) + HIDDEN_SUFFIX;
        Map<String, String> paramMap = context.getExternalContext().getRequestParameterMap();

        if (picklist.isDisabled()) {
            return;
        }

        String value = paramMap.get(hiddenClientId);
        if (value != null) {
            if (value.trim().equals("")) {

                ((EditableValueHolder) picklist).setSubmittedValue(new String[] {});

            } else {
                String[] reqValues = value.split(",");
                ((EditableValueHolder) picklist).setSubmittedValue(reqValues);
            }

        } else {
            ((EditableValueHolder) picklist).setSubmittedValue(new String[] {});
        }

    }

    private static boolean isTrue(Object obj) {

        if (!(obj instanceof Boolean)) {
            return false;
        }

        return ((Boolean) obj).booleanValue();

    }

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

        Object convertedValue = null;

        if (component instanceof UIPickList) {
            UIPickList pickList = (UIPickList) component;
            convertedValue = SelectUtils.getConvertedUISelectManyValue(context, pickList,
                    (String[]) submittedValue);
        }

        return convertedValue;
    }

    private void encodeRows(FacesContext context, UIPickList pickList, boolean source, ListStateFlags flags)
            throws IOException {

        List<SelectItem> selectItemsList = SelectUtils.getSelectItems(context, pickList);

        Converter converter = pickList.getConverter();

        List<Object> values = getValuesList(pickList); // PickListUtils.getValuesList(context, pickList, converter);

        List<SelectItem> selectItemsForSelectedValues = selectItemsForSelectedList(context, pickList,
                selectItemsList, values);
        List<SelectItem> selectItemsForAvailableList = selectItemsForAvailableList(context, pickList,
                selectItemsList, selectItemsForSelectedValues);

        flags.isSelectedList = !selectItemsForSelectedValues.isEmpty();
        flags.isAvailableList = !selectItemsForAvailableList.isEmpty();

        List<SelectItem> selectItemList = null;

        if (source) {
            selectItemList = selectItemsForAvailableList;
        } else {
            selectItemList = selectItemsForSelectedValues;
        }

        if (selectItemList != null) {

            SelectItem[] itemsList = selectItemList.toArray(new SelectItem[selectItemList.size()]);

            for (int i = 0; i < itemsList.length; i++) {

                SelectItem selectItem = itemsList[i];

                if (selectItem instanceof SelectItemGroup) {

                    SelectItem[] items = ((SelectItemGroup) selectItem).getSelectItems();
                    for (int j = 0; j < items.length; j++) {
                        encodeItem(context, pickList, converter, items[j], source, "group:" + j);
                    }

                } else {

                    encodeItem(context, pickList, converter, selectItem, source, Integer.toString(i));

                }

            }

        }

    }

    public void encodeItem(FacesContext context, UIComponent component, Converter converter, SelectItem selectItem,
            boolean source, String suff) throws IOException {

        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(HTML.TR_ELEMENT, component);

        String clientId = component.getClientId(context);

        if (source) {
            clientId += ":source:";
        }

        String id = clientId + ":" + suff;

        writer.writeAttribute("id", id, null);

        StringBuffer rowClassName = new StringBuffer();
        StringBuffer cellClassName = new StringBuffer();

        if (source) {

            rowClassName.append("rich-picklist-source-row");
            cellClassName.append("rich-picklist-source-cell");

        } else {

            rowClassName.append("rich-picklist-target-row");
            cellClassName.append("rich-picklist-target-cell");

        }

        writer.writeAttribute("class", rowClassName.toString(), null);
        writer.startElement(HTML.td_ELEM, component);
        writer.writeAttribute(HTML.class_ATTRIBUTE, cellClassName, null);

        Object width = component.getAttributes().get("width");
        if (width != null) {
            writer.writeAttribute("style", "width: " + HtmlUtil.qualifySize(width.toString()), null);
        }

        encodeSpacer(context, component, writer);

        boolean escape = isTrue(component.getAttributes().get("escape"));
        if (escape) {
            writer.writeText(selectItem.getLabel(), null);
        } else {
            writer.write(selectItem.getLabel());
        }

        String itemValue;
        if (converter != null) {
            itemValue = converter.getAsString(context, component, selectItem.getValue());
        } else {
            Object valueObject = selectItem.getValue();
            itemValue = (valueObject != null ? valueObject.toString() : "");
        }

        encodeItemValue(context, component, writer, id, itemValue);

        writer.endElement(HTML.td_ELEM);
        writer.endElement(HTML.TR_ELEMENT);

    }

    public void encodeTargetRows(FacesContext context, UIPickList pickList, ListStateFlags flags)
            throws IOException {
        encodeRows(context, pickList, false, flags);
    }

    public void encodeSourceRows(FacesContext context, UIPickList pickList, ListStateFlags flags)
            throws IOException {
        encodeRows(context, pickList, true, flags);
    }

    private void encodeItemValue(FacesContext context, UIComponent component, ResponseWriter writer, String id,
            String itemValue) throws IOException { //rowKey = i

        writer.startElement(HTML.INPUT_ELEM, component);
        writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
        writer.writeAttribute(HTML.NAME_ATTRIBUTE, id, null);

        StringBuffer value = new StringBuffer();

        value.append(itemValue);

        writer.writeAttribute(HTML.value_ATTRIBUTE, value.toString(), null);
        writer.writeAttribute(HTML.id_ATTRIBUTE, id + "StateInput", null);
        writer.endElement(HTML.INPUT_ELEM);

    }

    protected void encodeSpacer(FacesContext context, UIComponent component, ResponseWriter writer)
            throws IOException {
        writer.startElement(HTML.IMG_ELEMENT, component);

        writer.writeAttribute(HTML.src_ATTRIBUTE,
                getResource("/org/richfaces/renderkit/html/images/spacer.gif").getUri(context, null), null);
        writer.writeAttribute(HTML.alt_ATTRIBUTE, " ", null);
        writer.writeAttribute(HTML.style_ATTRIBUTE, "width:1px;height:1px;", null);

        writer.endElement(HTML.IMG_ELEMENT);
    }

    public void encodeHiddenField(FacesContext context, UIPickList pickList) throws IOException {

        Converter converter = pickList.getConverter();
        List<String> lookupList = new ArrayList<String>();
        List<Object> values = getValuesList(pickList);

        for (Object value : values) {
            if (converter != null) {
                lookupList.add(converter.getAsString(context, pickList, value));
            } else {
                lookupList.add(value != null ? value.toString() : "");
            }
        }

        encodeHiddenField(context, pickList, lookupList);

    }

    private void encodeHiddenField(FacesContext context, UIComponent component, List<String> lookupList)
            throws IOException {

        String hiddenFieldCliendId = component.getClientId(context) + HIDDEN_SUFFIX;

        ResponseWriter writer = context.getResponseWriter();

        writer.startElement(HTML.INPUT_ELEM, component);
        writer.writeAttribute(HTML.TYPE_ATTR, "hidden", "type");
        writer.writeAttribute(HTML.id_ATTRIBUTE, hiddenFieldCliendId, "id");
        writer.writeAttribute("name", hiddenFieldCliendId, null);

        StringBuffer sb = new StringBuffer();

        int n = 0;

        for (Iterator<String> i = lookupList.iterator(); i.hasNext();) {

            if (n > 0) {
                sb.append(",");
            }

            String value = i.next();
            sb.append(value);
            n++;

        }

        writer.writeAttribute(HTML.value_ATTRIBUTE, sb.toString(), null);
        writer.endElement(HTML.INPUT_ELEM);

    }

    protected Class<? extends UIComponent> getComponentClass() {
        return UIPickList.class;
    }

    public String getAsEventHandler(FacesContext context, UIComponent component, String attributeName) {

        String event = (String) component.getAttributes().get(attributeName);
        ScriptString result = JSReference.NULL;

        if (event != null) {

            event = event.trim();

            if (event.length() != 0) {

                JSFunctionDefinition function = new JSFunctionDefinition();
                function.addParameter("event");
                function.addToBody(event);

                result = function;
            }

        }

        return ScriptUtils.toScript(result);

    }

    public String getColumnClassesAsJSArray(FacesContext context, UIComponent component) {
        return ScriptUtils.toScript(getClassesAsList(context, component, "columnClasses"));
    }

    public String getRowClassesAsJSArray(FacesContext context, UIComponent component) {
        return ScriptUtils.toScript(getClassesAsList(context, component, "rowClasses"));
    }

    protected List<String> getClassesAsList(FacesContext context, UIComponent component, String attr) {
        String value = (String) ((UIComponent) component).getAttributes().get(attr);

        if (value != null && (value.length() != 0)) {
            return Arrays.asList(value.split(","));
        }

        return null;
    }

    public void encodePickListControlsFacets(FacesContext context, UIComponent component,
            ListStateFlags listStateFlags) throws IOException {
        boolean needsStrut = true;

        String clientId = component.getClientId(context);
        boolean enable = false;

        ResponseWriter writer = context.getResponseWriter();
        boolean componentDisabled = ((UIPickList) component).isDisabled();

        for (int i = 0; i < SHUTTLE_HELPERS.length; i++) {
            //Conditionally render the control based on the return value of the control's corresponding helper.isRendered method
            if (SHUTTLE_HELPERS[i].isRendered(context, component)) {
                needsStrut = false;

                OrderingComponentRendererBase.ControlsHelper helper = SHUTTLE_HELPERS[i];

                boolean isDisabled = helper.getButtonStyleClass()
                        .equals(PickListControlsHelper.DISABLED_STYLE_PREF);

                if (helper.getBundlePropertyName().equals(PickListControlsHelper.BUNDLE_REMOVE_ALL_LABEL)) {

                    enable = listStateFlags.isSelectedList;
                    enable = (enable != isDisabled) || (isDisabled && componentDisabled) ? true : false;

                } else if (helper.getBundlePropertyName().equals(PickListControlsHelper.BUNDLE_COPY_ALL_LABEL)) {

                    enable = listStateFlags.isAvailableList;
                    enable = (enable != isDisabled) || (isDisabled && componentDisabled) ? true : false;

                } else {

                    if (helper.getButtonStyleClass().equals(PickListControlsHelper.DISABLED_STYLE_PREF)) {
                        enable = true;
                    } else {
                        enable = false;
                    }

                }

                encodeControlFacet(context, component, SHUTTLE_HELPERS[i], clientId, writer, enable,
                        "rich-list-picklist-button", " rich-picklist-control");
            }
        }

        if (needsStrut) {
            writer.startElement(HTML.SPAN_ELEM, component);
            writer.endElement(HTML.SPAN_ELEM);
        }
    }

    protected void encodeControlFacet(FacesContext context, UIComponent component,
            OrderingComponentRendererBase.ControlsHelper helper, String clientId, ResponseWriter writer,
            boolean enabled, String baseStyle, String baseControlStyle) throws IOException {

        renderDefaultControl(context, component, writer, helper, clientId, enabled, baseStyle, baseControlStyle);

    }

    protected ClassLoader getCurrentLoader(Object fallbackClass) {

        ClassLoader loader = Thread.currentThread().getContextClassLoader();

        if (loader == null) {
            loader = fallbackClass.getClass().getClassLoader();
        }

        return loader;

    }

    protected String findLocalisedLabel(FacesContext context, String propertyId, String bundleName) {
        String label = null;
        Locale locale = null;
        String userBundleName = null;
        ResourceBundle bundle = null;

        UIViewRoot viewRoot = context.getViewRoot();
        if (viewRoot != null) {
            locale = viewRoot.getLocale();
        } else {
            locale = Locale.getDefault();
        }

        if (locale != null) {

            try {

                if (null != (userBundleName = context.getApplication().getMessageBundle())) {
                    bundle = ResourceBundle.getBundle(userBundleName, locale, getCurrentLoader(userBundleName));

                    if (bundle != null) {
                        label = bundle.getString(propertyId);
                    }
                }

            } catch (MissingResourceException e) {

                if (logger.isDebugEnabled()) {
                    logger.debug("Can't find bundle properties file " + userBundleName + " " + locale.getLanguage()
                            + " " + locale.getCountry());
                }

            }

            if (label == null && bundleName != null) {

                try {

                    bundle = ResourceBundle.getBundle(bundleName, locale, getCurrentLoader(bundleName));
                    if (bundle != null) {
                        label = bundle.getString(propertyId);
                    }

                } catch (MissingResourceException e) {

                    if (logger.isDebugEnabled()) {
                        logger.debug("Can't find bundle properties file " + bundleName + " " + locale.getLanguage()
                                + " " + locale.getCountry());
                    }

                }
            }

        }

        return label;
    }

    protected void renderDefaultControl(FacesContext context, UIComponent component, ResponseWriter writer,
            OrderingComponentRendererBase.ControlsHelper helper, String clientId, boolean enabled, String baseStyle,
            String baseControlStyle) throws IOException {

        UIComponent facet = component.getFacet(getAltAttribbute(helper));

        boolean useFacet = (facet != null && facet.isRendered());

        String customEvent = null;

        Map<String, Object> attributes = component.getAttributes();

        if (helper.customEvent != null) {
            customEvent = (String) attributes.get(helper.customEvent);
        }

        String styleFromAttribute = (String) attributes.get(helper.styleFromAttribute);
        String baseStyleLight = baseStyle.concat("-light");
        String baseStylePress = baseStyle.concat("-press");
        String currentStyle = baseControlStyle + helper.getStyleClassName();

        if (styleFromAttribute != null) {
            currentStyle = styleFromAttribute.concat(currentStyle);
        }

        String controlId = clientId + helper.getIdSuffix();
        writer.startElement(HTML.DIV_ELEM, component);
        writer.writeAttribute(HTML.id_ATTRIBUTE, controlId, null); // FIXME:
        writer.writeAttribute(HTML.class_ATTRIBUTE, currentStyle, null);

        String style = enabled ? "display:block;" : "display:none;";

        writer.writeAttribute(HTML.style_ATTRIBUTE, style, null);

        if (!useFacet) {

            writer.startElement(HTML.DIV_ELEM, component);
            writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + helper.getButtonStyleClass(), null);

            if (helper.enable) {
                writer.writeAttribute(HTML.onmouseover_ATTRIBUTE, "this.className='" + baseStyleLight + "'", null);
                writer.writeAttribute(HTML.onmousedown_ATTRIBUTE, "this.className='" + baseStylePress + "'", null);
                writer.writeAttribute(HTML.onmouseup_ATTRIBUTE, "this.className='" + baseStyle + "'", null);
                writer.writeAttribute(HTML.onmouseout_ATTRIBUTE, "this.className='" + baseStyle + "'", null);
            }

            if (!helper.enable) {
                //writer.writeAttribute(HTML.DISABLED_ATTR, "disabled", null);
                //writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + "-a-disabled", null);
                //writer.startElement(HTML.a_ELEMENT, component);
            } else {
                writer.startElement(HTML.a_ELEMENT, component);
                writer.writeAttribute(HTML.id_ATTRIBUTE, controlId + "link", null); // FIXME:
                writer.writeAttribute(HTML.onclick_ATTRIBUTE, "return false;", null);

                writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + "-selection", null);
                writer.writeAttribute(HTML.onblur_ATTRIBUTE, "Richfaces.Control.onblur(this);", null);
                writer.writeAttribute(HTML.onfocus_ATTRIBUTE, "Richfaces.Control.onfocus(this);", null);
            }

            writer.startElement(HTML.DIV_ELEM, component);
            writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + "-content", null);
        }

        if (customEvent != null) {
            writer.writeAttribute(HTML.onclick_ATTRIBUTE, customEvent, null);
        }

        if (useFacet) {
            renderChild(context, facet);
        } else {

            writer.startElement(HTML.IMG_ELEMENT, component);
            writer.writeAttribute(HTML.class_ATTRIBUTE, "rich-picklist-control-img", null);
            writer.writeAttribute(HTML.alt_ATTRIBUTE, component.getAttributes().get(helper.getTitle()), null);
            writer.writeAttribute(HTML.src_ATTRIBUTE, getResource(helper.getImageURI()).getUri(context, null),
                    null);
            writer.endElement(HTML.IMG_ELEMENT);

            if (getUtils().isBooleanAttribute(component, SHOW_LABELS_ATTRIBUTE_NAME)) {
                String label = (String) attributes.get(helper.getLabelAttributeName());
                if (label == null || label.equals("")) {
                    label = findLocalisedLabel(context, helper.getBundlePropertyName(), MESSAGE_BUNDLE_NAME);
                }

                if (label == null || label.equals("")) {
                    label = helper.getDefaultText();
                }

                writer.write(label);
            }
        }

        if (!useFacet) {
            writer.endElement(HTML.DIV_ELEM);

            if (helper.enable) {
                writer.endElement(HTML.a_ELEMENT);
            }

            writer.endElement(HTML.DIV_ELEM);
        }

        writer.endElement(HTML.DIV_ELEM);
    }

    private String getAltAttribbute(OrderingComponentRendererBase.ControlsHelper helper) {

        return helper.getFacetName() != null ? helper.getFacetName() : " ";

    }

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

        AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);

        Set<String> areas = ajaxContext.getAjaxRenderedAreas();
        String clientId = component.getClientId(context);

        if (ajaxContext.isAjaxRequest() && areas.contains(clientId)) {
            areas.add(clientId + "script");
        }

    }

}