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

org.icefaces.ace.component.checkboxbuttons.CheckboxButtonsRenderer Maven / Gradle / Ivy

The newest version!
/*
 * Original Code Copyright Prime Technology.
 * Subsequent Code Modifications Copyright 2011-2014 ICEsoft Technologies Canada Corp. (c)
 *
 * 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.
 *
 * NOTE THIS CODE HAS BEEN MODIFIED FROM ORIGINAL FORM
 *
 * Subsequent Code Modifications have been made and contributed by ICEsoft Technologies Canada Corp. (c).
 *
 * Code Modification 1: Integrated with ICEfaces Advanced Component Environment.
 * Contributors: ICEsoft Technologies Canada Corp. (c)
 *
 * Code Modification 2: (ICE-6978) Used JSONBuilder to add the functionality of escaping JS output.
 * Contributors: ICEsoft Technologies Canada Corp. (c)
 */
package org.icefaces.ace.component.checkboxbuttons;


import org.icefaces.ace.component.checkboxbutton.CheckboxButtonRenderer;
import org.icefaces.render.MandatoryResourceComponent;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;
import java.lang.String;
import java.util.*;
import org.icefaces.ace.util.*;
import javax.faces.component.UIParameter;
import javax.faces.convert.ConverterException;
import org.icefaces.util.EnvUtils;
import org.icefaces.util.JavaScriptRunner;

import javax.faces.model.SelectItem;
import javax.faces.convert.Converter;
import javax.el.ExpressionFactory;
import javax.el.ELException;
import java.lang.reflect.Array;
import javax.el.ValueExpression;
import javax.faces.component.UISelectMany;
import javax.faces.FacesException;
import javax.faces.application.Application;

@MandatoryResourceComponent(tagName = "checkboxButtons", value = "org.icefaces.ace.component.checkboxbuttons.CheckboxButtons")
public class CheckboxButtonsRenderer extends CheckboxButtonRenderer {

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

        CheckboxButtons checkboxButtons = (CheckboxButtons) component;
		String clientId = checkboxButtons.getClientId(context);

		Map requestParameterValuesMap =
			  context.getExternalContext().getRequestParameterValuesMap();
		if (requestParameterValuesMap.containsKey(clientId)) {
			String newValues[] = requestParameterValuesMap.get(clientId);
			checkboxButtons.setSubmittedValue(newValues);
		} else {
			// Use the empty array, not null, to distinguish
			// between a deselected UISelectMany and a disabled one
			checkboxButtons.setSubmittedValue(new String[0]);
		}
		decodeBehaviors(context, checkboxButtons);
    }


    @Override
    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        CheckboxButtons checkboxButtons = (CheckboxButtons) component;
        String style = (style = checkboxButtons.getStyle()) == null ? "" : style.trim();
        String styleClass = (styleClass = checkboxButtons.getStyleClass()) == null ? "" : styleClass.trim();
        styleClass += (styleClass.length() > 0 ? " " : "") + "ice-ace-checkboxbuttons ui-widget ui-widget-content ui-corner-all " +getStateStyleClasses(checkboxButtons);
		styleClass += "pageDirection".equalsIgnoreCase(checkboxButtons.getLayout()) ? " ice-ace-checkboxbuttons-vertical" : "";

        writer.startElement("div", component);
        writer.writeAttribute("id", component.getClientId(context), "id");
        if (style.length() > 0) {
            writer.writeAttribute("style", style, "style");
        }
        writer.writeAttribute("class", styleClass, "styleClass");

        UIComponent headerFacet = component.getFacet("header");
        String headerText = (headerText = checkboxButtons.getHeader()) == null ? "" : headerText.trim();

        if (headerFacet != null || headerText.length() > 0) {
            writer.startElement("div", null);
            writer.writeAttribute("class", "ui-widget-header ui-corner-top", null);
            if (headerFacet != null) {
                renderChild(context, headerFacet);
            } else if (headerText.length() > 0) {
                writer.write(headerText);
            }
            writer.endElement("div");
        }

		boolean required = checkboxButtons.isRequired();
		String indicatorPosition = checkboxButtons.getIndicatorPosition();
		String indicator = required ? checkboxButtons.getRequiredIndicator() : checkboxButtons.getOptionalIndicator();
		if ("left".equalsIgnoreCase(indicatorPosition)
			|| "top".equalsIgnoreCase(indicatorPosition)) {
				if (indicator != null) {
					writer.startElement("span", null);
					writer.writeAttribute("class", "ice-indicator", null);
					writer.write(indicator);
					writer.endElement("span");
				}
		}
		if ("top".equalsIgnoreCase(indicatorPosition)) {
			writer.startElement("br", null);
			writer.endElement("br");
		}

		// render buttons
		Object currentSelections = getCurrentSelectedValues(checkboxButtons);
        Converter converter = checkboxButtons.getConverter();
		SelectItemsIterator selectItemsIterator = new SelectItemsIterator(context, checkboxButtons);
		int i = 0;
		if (!checkboxButtons.isValid()) {
			while (selectItemsIterator.hasNext()) {
				encodeButton(context, checkboxButtons, i++, selectItemsIterator.next(), converter, checkboxButtons.getSubmittedValue());
			}
            encodeSelect(context, checkboxButtons, new SelectItemsIterator(context, checkboxButtons), converter, checkboxButtons.getSubmittedValue());
		} else {
			while (selectItemsIterator.hasNext()) {
				encodeButton(context, checkboxButtons, i++, selectItemsIterator.next(), converter, currentSelections);
			}
            encodeSelect(context, checkboxButtons, new SelectItemsIterator(context, checkboxButtons), converter, currentSelections);
		}

		if ("bottom".equalsIgnoreCase(indicatorPosition)) {
			writer.startElement("br", null);
			writer.endElement("br");
		}
		if ("right".equalsIgnoreCase(indicatorPosition)
			|| "bottom".equalsIgnoreCase(indicatorPosition)) {
				if (indicator != null) {
					writer.startElement("span", null);
					writer.writeAttribute("class", "ice-indicator", null);
					writer.write(indicator);
					writer.endElement("span");
				}
		}

		writer.endElement("div");
    }

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

    private void encodeSelect(FacesContext facesContext, CheckboxButtons checkboxButtons, SelectItemsIterator selectItemsIterator, Converter converter, Object currentSelections) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        String id = checkboxButtons.getClientId(facesContext);
        writer.startElement("select", checkboxButtons);
        writer.writeAttribute("id", id + "_options", null);
        writer.writeAttribute("name", id, null);
        writer.writeAttribute("multiple", "multiple", null);
        writer.writeAttribute("style", "visibility: hidden; width: 1px; height: 1px; padding: 0; margin 0; border: none;", null);
        int i = 0;
        while (selectItemsIterator.hasNext()) {
            SelectItem item = selectItemsIterator.next();
            Object value = getConvertedValueForClient(facesContext, checkboxButtons, item.getValue());
            boolean selected = isSelected(facesContext, checkboxButtons, value, currentSelections, converter);
            String clientId = checkboxButtons.getClientId(facesContext) + ":" + i;

            writer.startElement("option", checkboxButtons);
            if (selected) writer.writeAttribute("selected", "selected", null);
            writer.writeAttribute("title", clientId, null);
            writer.writeAttribute("value", value, null);
            writer.writeText(value, null);
            writer.endElement("option");
            i++;
        }
        writer.endElement("select");
    }

	private void encodeButton(FacesContext facesContext, CheckboxButtons checkboxButtons, int index, SelectItem item, Converter converter, Object currentSelections) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();

        String clientId = checkboxButtons.getClientId(facesContext) + ":" + index;
		String labelPosition = checkboxButtons.getLabelPosition();

        String label = item.getLabel();
		Object value = getConvertedValueForClient(facesContext, checkboxButtons, item.getValue());
		boolean selected = isSelected(facesContext, checkboxButtons, value, currentSelections, converter);

        String firstWrapperClass = "ui-button ui-widget";
        String secondWrapperClass = "first-child";
        boolean ariaEnabled = EnvUtils.isAriaEnabled(facesContext);

        // Root Container
        writer.startElement(HTML.DIV_ELEM, checkboxButtons);
        writer.writeAttribute(HTML.ID_ATTR, clientId, null);
		renderResetSettings(facesContext, (UIComponent)checkboxButtons);
//        ComponentUtils.enableOnElementUpdateNotify(writer, clientId);

        boolean disabled = item.isDisabled() || checkboxButtons.isDisabled();
        writer.writeAttribute(HTML.CLASS_ATTR, "ice-checkboxbutton" + (disabled ? " ui-state-disabled" : ""), null);
        String script = getScript(facesContext, writer, checkboxButtons, clientId, disabled);
		writer.writeAttribute("data-init", "if (!document.getElementById('" + clientId + "').widget) " + script, null);
        encodeScript(writer, EventType.HOVER);

		if (label != null) {
			if ("left".equalsIgnoreCase(labelPosition)
					|| "top".equalsIgnoreCase(labelPosition)) {
				writer.startElement("label", null);
				writer.writeAttribute("id", "label_" + clientId, null);
				writer.writeAttribute("for", clientId, null);
				writer.write(label);
				writer.endElement("label");
			}
			if ("top".equalsIgnoreCase(labelPosition)) {
				writer.startElement("br", null);
				writer.endElement("br");
			}
		}

        // First Wrapper
        writer.startElement(HTML.SPAN_ELEM, null);
        writer.writeAttribute(HTML.CLASS_ATTR, firstWrapperClass, null);

        // Second Wrapper
        writer.startElement(HTML.SPAN_ELEM, null);
        writer.writeAttribute(HTML.CLASS_ATTR, secondWrapperClass, null);

        if (ariaEnabled) {
			writer.writeAttribute(HTML.ROLE_ATTR, "checkbox", null);
			writer.writeAttribute(HTML.ARIA_DISABLED_ATTR, item.isDisabled(), null);
		}

        // Button Element
        writer.startElement(HTML.BUTTON_ELEM, null);
        writer.writeAttribute(HTML.TYPE_ATTR, "button", null);
        String buttonId = clientId + "_button";
        writer.writeAttribute(HTML.ID_ATTR, buttonId, null);
        writer.writeAttribute(HTML.NAME_ATTR, buttonId, null);
		String selectedClass = "";
		selectedClass = (selected ? "ice-checkboxbutton-checked" : "ice-checkboxbutton-unchecked");
		writer.writeAttribute(HTML.CLASS_ATTR, "ui-corner-all ui-widget-content " + selectedClass, null);

		if (ariaEnabled) writer.writeAttribute(HTML.TABINDEX_ATTR, "0", null);

        encodeButtonStyleClass(writer,false, disabled);
        encodeScript(writer, EventType.FOCUS);


        if (label != null && "inField".equalsIgnoreCase(checkboxButtons.getLabelPosition())) {
            writer.startElement(HTML.SPAN_ELEM, null);
            writer.writeAttribute(HTML.CLASS_ATTR, "ui-label", null);
            writer.write(label);
            writer.endElement(HTML.SPAN_ELEM);
        } else {
            writer.startElement(HTML.SPAN_ELEM, null);
            encodeIconStyle(writer, selected);
            writer.endElement(HTML.SPAN_ELEM);
        }

        writer.endElement(HTML.BUTTON_ELEM);
        writer.endElement(HTML.SPAN_ELEM);
        writer.endElement(HTML.SPAN_ELEM);

		if (label != null) {
			if ("bottom".equalsIgnoreCase(labelPosition)) {
				writer.startElement("br", null);
				writer.endElement("br");
			}
			if ("right".equalsIgnoreCase(labelPosition)
					|| "bottom".equalsIgnoreCase(labelPosition)) {
				writer.startElement("label", null);
				writer.writeAttribute("id", "label_" + clientId, null);
				writer.writeAttribute("for", clientId, null);
				writer.write(label);
				writer.endElement("label");
			}
		}

		JavaScriptRunner.runScript(facesContext, "ice.ace.checkboxbutton.register('"+clientId+"','"+checkboxButtons.getClientId(facesContext)+"');");

		writer.endElement(HTML.DIV_ELEM);

		JavaScriptRunner.runScript(facesContext, "ice.ace.registerLazyComponent('" + clientId + "');");
	}


    private String getScript(FacesContext facesContext, ResponseWriter writer,
                              CheckboxButtons checkboxButtons, String clientId, boolean disabled) throws IOException {
        String groupId = checkboxButtons.getClientId(facesContext);

        boolean ariaEnabled = EnvUtils.isAriaEnabled(facesContext);
        JSONBuilder jb = JSONBuilder.create();
        List uiParamChildren = Utils.captureParameters(checkboxButtons);

        jb.beginFunction("ice.ace.lazy")
          .item("checkboxbutton")
          .beginArray()
          .item(clientId)
          .beginMap()
          .entry("groupId", groupId)
          .entry("ariaEnabled", ariaEnabled)
          .entry("checkboxButtons", checkboxButtons.getClientId(facesContext))
          .entry("mutuallyExclusive", checkboxButtons.isMutuallyExclusive());

		if (disabled) jb.entry("disabled", true);

        if (uiParamChildren != null) {
            jb.beginMap("uiParams");
            for (UIParameter p : uiParamChildren)
                jb.entry(p.getName(), (String)p.getValue());
            jb.endMap();
        }

        encodeClientBehaviors(facesContext, checkboxButtons, jb);

        jb.endMap().endArray().endFunction();
		
		return jb.toString();
	}


    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()) {
                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 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;

    }

    protected Object getCurrentSelectedValues(CheckboxButtons checkboxButtons) {

            Object value = checkboxButtons.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;
    }

    @Override
    public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
		UISelectMany uiSelectMany = (UISelectMany) component;
		String[] newValues = (String[]) submittedValue;

		// 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("Couldn't convert " + values.toString()
				+ " (" + valueExpression.getExpressionString() + ")");
		}

		return result;
    }

	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 = 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 not 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 = 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("Couldn't convert " + valueStr.toString()
					+ " (null Converter)");
			}
		}

		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++) {
				Array.set(result, i, converter.getAsObject(context,
														   uiSelectMany,
														   newValues[i]));
			}
		}
		return result;
	}

    protected Converter getConverterForClass(Class converterClass, FacesContext context) {
        if (converterClass == null) {
            return null;
        }
        try {            
            Application application = context.getApplication();
            return (application.createConverter(converterClass));
        } catch (Exception e) {
            return (null);
        }
    }

    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);
        }

    }

	public String getConvertedValueForClient(FacesContext context, UIComponent component, Object value) throws ConverterException {
		CheckboxButtons checkboxButtons = (CheckboxButtons) component;
		Converter converter = checkboxButtons.getConverter();
		
		if(converter != null) {
			return converter.getAsString(context, checkboxButtons, value);
		} else {
			ValueExpression ve = checkboxButtons.getValueExpression("value");

			if(ve != null) {
				Class valueType = ve.getType(context.getELContext());
				Converter converterForType = context.getApplication().createConverter(valueType);

				if(converterForType != null) {
					if (converterForType instanceof javax.faces.convert.EnumConverter && "".equals(value)) return converterForType.getAsString(context, checkboxButtons, null);
					return converterForType.getAsString(context, checkboxButtons, value);
				}
			}
		}
		
		return (value != null ? value.toString() : "");
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy