You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2015/04/25 22:42:19 UTC
svn commit: r1676067 -
/myfaces/tobago/branches/tobago-3.0.x/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/SelectManyRendererBase.java
Author: weber
Date: Sat Apr 25 20:42:19 2015
New Revision: 1676067
URL: http://svn.apache.org/r1676067
Log:
TOBAGO-1453 - SelectManyRendererBase should support Collection: merge trunk Revision #1676038 to 3.0 branch
Modified:
myfaces/tobago/branches/tobago-3.0.x/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/SelectManyRendererBase.java
Modified: myfaces/tobago/branches/tobago-3.0.x/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/SelectManyRendererBase.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/branches/tobago-3.0.x/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/SelectManyRendererBase.java?rev=1676067&r1=1676066&r2=1676067&view=diff
==============================================================================
--- myfaces/tobago/branches/tobago-3.0.x/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/SelectManyRendererBase.java (original)
+++ myfaces/tobago/branches/tobago-3.0.x/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/SelectManyRendererBase.java Sat Apr 25 20:42:19 2015
@@ -24,17 +24,34 @@ import org.apache.myfaces.tobago.util.Co
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.el.ValueExpression;
import javax.faces.FacesException;
+import javax.faces.application.ProjectStage;
import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
+import javax.faces.component.UISelectItem;
+import javax.faces.component.UISelectItems;
import javax.faces.component.UISelectMany;
+import javax.faces.component.UIViewRoot;
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.lang.reflect.Array;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
public class SelectManyRendererBase extends LayoutComponentRendererBase {
@@ -67,7 +84,6 @@ public class SelectManyRendererBase exte
}
}
- // the following is copied from myfaces shared RendererUtils
public Object getConvertedValue(
final FacesContext facesContext, final UIComponent component, final Object submittedValue)
throws ConverterException {
@@ -76,125 +92,704 @@ public class SelectManyRendererBase exte
return null;
} else {
if (!(submittedValue instanceof String[])) {
- throw new ConverterException("Submitted value of type String[] for component : "
+ throw new ConverterException("Submitted value not of type String[] for component : "
+ component.getClientId(facesContext) + "expected");
}
}
return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue);
}
- private Object getConvertedUISelectManyValue(final FacesContext facesContext,
- final UISelectMany component,
- final String[] submittedValue)
- throws ConverterException {
+ // #################################################################################################################
+ // #################################################################################################################
+ // ### The following methods and classes are copied from myfaces api 2.2.8,
+ // ### slightly modified to compile in this context.
+ // ### We copy this to avoid the dependency
+ // #################################################################################################################
+ // #################################################################################################################
+
+ // #################################################################################################################
+ // ### BEGIN copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_SharedRendererUtils.java
+ // #################################################################################################################
+ static final String COLLECTION_TYPE_KEY = "collectionType";
+ static final String VALUE_TYPE_KEY = "valueType";
+
+ static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,
+ String[] submittedValue) throws ConverterException {
+ return getConvertedUISelectManyValue(facesContext, component,
+ submittedValue, false);
+ }
+
+ /**
+ * Gets the converted value of a UISelectMany component.
+ * If the considerValueType is true, this method will also consider the
+ * valueType attribute of Tomahawk UISelectMany components.
+ *
+ * @param facesContext
+ * @param component
+ * @param submittedValue
+ * @param considerValueType
+ * @return
+ * @throws ConverterException
+ */
+ static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,
+ String[] submittedValue, boolean considerValueType) throws ConverterException {
// Attention!
- // This code is duplicated in jsfapi component package.
+ // This code is duplicated in shared renderkit package (except for considerValueType).
// If you change something here please do the same in the other class!
if (submittedValue == null) {
throw new NullPointerException("submittedValue");
}
- final ValueBinding vb = component.getValueBinding("value");
- Class valueType = null;
- Class arrayComponentType = null;
- if (vb != null) {
- valueType = vb.getType(facesContext);
- if (valueType != null && valueType.isArray()) {
- arrayComponentType = valueType.getComponentType();
- }
- }
+ ValueExpression expression = component.getValueExpression("value");
+ Object targetForConvertedValues = null;
+ // if the component has an attached converter, use it
Converter converter = component.getConverter();
+ // at this point the valueType attribute is handled in shared.
+ if (converter == null && considerValueType) {
+ // try to get a converter from the valueType attribute
+ converter = getValueTypeConverter(facesContext, component);
+ }
+
+ if (expression != null) {
+ Class<?> modelType = expression
+ .getType(facesContext.getELContext());
+ if (modelType == null) {
+ // FIXME temporal workaround for MYFACES-2552
+ return submittedValue;
+ } else if (modelType.isArray()) {
+ // the target should be an array
+ Class<?> componentType = modelType.getComponentType();
+ // check for optimization if the target is
+ // a string array --> no conversion needed
+ if (String.class.equals(componentType)) {
+ return submittedValue;
+ }
if (converter == null) {
- if (valueType == null) {
- // No converter, and no idea of expected type
- // --> return the submitted String array
+ // the compononent does not have an attached converter
+ // --> try to get a registered-by-class converter
+ converter = facesContext.getApplication().createConverter(
+ componentType);
+
+ if (converter == null && !Object.class.equals(componentType)) {
+ // could not obtain a Converter
+ // --> check if we maybe do not really have to convert
+
+ // target is not an Object array
+ // and not a String array (checked some lines above)
+ // and we do not have a Converter
+ throw new ConverterException(
+ "Could not obtain a Converter for "
+ + componentType.getName());
+ }
+ }
+ // instantiate the array
+ targetForConvertedValues = Array.newInstance(componentType,
+ submittedValue.length);
+ } else if (Collection.class.isAssignableFrom(modelType) || Object.class.equals(modelType)) {
+ if (converter == null) {
+ // try to get the by-type-converter from the type of the SelectItems
+ SelectItemsIterator iterator = new SelectItemsIterator(component, facesContext);
+ converter = getSelectItemsValueConverter(iterator, facesContext);
+ }
+
+ Object collectionTypeAttr = component.getAttributes().get(
+ COLLECTION_TYPE_KEY);
+ if (collectionTypeAttr != null) {
+ Class<?> collectionType = getClassFromAttribute(facesContext, collectionTypeAttr);
+ if (collectionType == null) {
+ throw new FacesException(
+ "The attribute "
+ + COLLECTION_TYPE_KEY
+ + " of component "
+ + component.getClientId(facesContext)
+ + " does not evaluate to a "
+ + "String, a Class object or a ValueExpression pointing "
+ + "to a String or a Class object.");
+ }
+ // now we have a collectionType --> but is it really some kind of Collection
+ if (!Collection.class.isAssignableFrom(collectionType)) {
+ throw new FacesException("The attribute "
+ + COLLECTION_TYPE_KEY + " of component "
+ + component.getClientId(facesContext)
+ + " does not point to a valid type of Collection.");
+ }
+ // now we have a real collectionType --> try to instantiate it
+ try {
+ targetForConvertedValues = collectionType.newInstance();
+ } catch (Exception e) {
+ throw new FacesException("The Collection "
+ + collectionType.getName()
+ + "can not be instantiated.", e);
+ }
+ } else if (Collection.class.isAssignableFrom(modelType)) {
+ // component.getValue() will implement Collection at this point
+ Collection<?> componentValue = (Collection<?>) component
+ .getValue();
+ // can we clone the Collection
+ if (componentValue instanceof Cloneable) {
+ // clone method of Object is protected --> use reflection
+ try {
+ Method cloneMethod = componentValue.getClass()
+ .getMethod("clone");
+ Collection<?> clone = (Collection<?>) cloneMethod
+ .invoke(componentValue);
+ clone.clear();
+ targetForConvertedValues = clone;
+ } catch (Exception e) {
+ LOG.error("Could not clone " + componentValue.getClass().getName(), e);
+ }
+ }
+
+ // if clone did not work
+ if (targetForConvertedValues == null) {
+ // try to create the (concrete) collection from modelType
+ // or with the class object of componentValue (if any)
+ try {
+ targetForConvertedValues = (componentValue != null
+ ? componentValue.getClass()
+ : modelType).newInstance();
+ } catch (Exception e) {
+ // this did not work either
+ // use the standard concrete type
+ if (SortedSet.class.isAssignableFrom(modelType)) {
+ targetForConvertedValues = new TreeSet();
+ } else if (Queue.class.isAssignableFrom(modelType)) {
+ targetForConvertedValues = new LinkedList();
+ } else if (Set.class.isAssignableFrom(modelType)) {
+ targetForConvertedValues = new HashSet(
+ submittedValue.length);
+ } else {
+ targetForConvertedValues = new ArrayList(
+ submittedValue.length);
+ }
+ }
+ }
+ } else /* if (Object.class.equals(modelType)) */ {
+ // a modelType of Object is also permitted, in order to support
+ // managed bean properties of type Object
+
+ // optimization: if we don't have a converter, we can return the submittedValue
+ if (converter == null) {
return submittedValue;
}
- if (List.class.isAssignableFrom(valueType)) {
- // expected type is a List
- // --> according to javadoc of UISelectMany we assume that the element type
- // is java.lang.String, and copy the String array to a new List
- return Arrays.asList(submittedValue);
+ targetForConvertedValues = new Object[submittedValue.length];
}
+ } else {
+ // the expression does neither point to an array nor to a collection
+ throw new ConverterException(
+ "ValueExpression for UISelectMany must be of type Collection or Array.");
+ }
+ } else {
+ targetForConvertedValues = new Object[submittedValue.length];
+ }
- if (arrayComponentType == null) {
- throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
+ // convert the values with the selected converter (if any)
+ // and store them in targetForConvertedValues
+ boolean isArray = (targetForConvertedValues.getClass().isArray());
+ for (int i = 0; i < submittedValue.length; i++) {
+ // get the value
+ Object value;
+ if (converter != null) {
+ value = converter.getAsObject(facesContext, component,
+ submittedValue[i]);
+ } else {
+ value = submittedValue[i];
+ }
+ // store it in targetForConvertedValues
+ if (isArray) {
+ Array.set(targetForConvertedValues, i, value);
+ } else {
+ ((Collection) targetForConvertedValues).add(value);
}
+ }
+
+ return targetForConvertedValues;
+ }
+
+ /**
+ * Gets a Class object from a given component attribute. The attribute can
+ * be a ValueExpression (that evaluates to a String or a Class) or a
+ * String (that is a fully qualified Java class name) or a Class object.
+ *
+ * @param facesContext
+ * @param attribute
+ * @return
+ * @throws FacesException if the value is a String and the represented
+ * class cannot be found
+ */
+ static Class<?> getClassFromAttribute(FacesContext facesContext,
+ Object attribute) throws FacesException {
+ // Attention!
+ // This code is duplicated in shared renderkit package.
+ // If you change something here please do the same in the other class!
+
+ Class<?> type = null;
- if (String.class.equals(arrayComponentType)) {
- return submittedValue; //No conversion needed for String type
+ // if there is a value, it must be a ...
+ // ... a ValueExpression that evaluates to a String or a Class
+ if (attribute instanceof ValueExpression) {
+ // get the value of the ValueExpression
+ attribute = ((ValueExpression) attribute)
+ .getValue(facesContext.getELContext());
}
- if (Object.class.equals(arrayComponentType)) {
- return submittedValue; //No conversion for Object class
+ // ... String that is a fully qualified Java class name
+ if (attribute instanceof String) {
+ try {
+ type = Class.forName((String) attribute);
+ } catch (ClassNotFoundException cnfe) {
+ throw new FacesException(
+ "Unable to find class "
+ + attribute
+ + " on the classpath.", cnfe);
+ }
+ } else if (attribute instanceof Class) {
+ // ... a Class object
+ type = (Class<?>) attribute;
+ }
+
+ return type;
+ }
+
+ /**
+ * Uses the valueType attribute of the given UISelectMany component to
+ * get a by-type converter.
+ *
+ * @param facesContext
+ * @param component
+ * @return
+ */
+ static Converter getValueTypeConverter(FacesContext facesContext, UISelectMany component) {
+ Converter converter = null;
+
+ Object valueTypeAttr = component.getAttributes().get(VALUE_TYPE_KEY);
+ if (valueTypeAttr != null) {
+ // treat the valueType attribute exactly like the collectionType attribute
+ Class<?> valueType = getClassFromAttribute(facesContext, valueTypeAttr);
+ if (valueType == null) {
+ throw new FacesException(
+ "The attribute "
+ + VALUE_TYPE_KEY
+ + " of component "
+ + component.getClientId(facesContext)
+ + " does not evaluate to a "
+ + "String, a Class object or a ValueExpression pointing "
+ + "to a String or a Class object.");
+ }
+ // now we have a valid valueType
+ // --> try to get a registered-by-class converter
+ converter = facesContext.getApplication().createConverter(valueType);
+
+ if (converter == null) {
+ facesContext.getExternalContext().log("Found attribute valueType on component "
+ + getPathToComponent(component)
+ + ", but could not get a by-type converter for type "
+ + valueType.getName());
+ }
+ }
+
+ return converter;
+ }
+
+ /**
+ * Iterates through the SelectItems with the given Iterator and tries to obtain
+ * a by-class-converter based on the Class of SelectItem.getValue().
+ *
+ * @param iterator
+ * @param facesContext
+ * @return The first suitable Converter for the given SelectItems or null.
+ */
+ static Converter getSelectItemsValueConverter(Iterator<SelectItem> iterator, FacesContext facesContext) {
+ // Attention!
+ // This code is duplicated in jsfapi component package.
+ // If you change something here please do the same in the other class!
+
+ Converter converter = null;
+ while (converter == null && iterator.hasNext()) {
+ SelectItem item = iterator.next();
+ if (item instanceof SelectItemGroup) {
+ Iterator<SelectItem> groupIterator = Arrays.asList(
+ ((SelectItemGroup) item).getSelectItems()).iterator();
+ converter = getSelectItemsValueConverter(groupIterator, facesContext);
+ } else {
+ Class<?> selectItemsType = item.getValue().getClass();
+
+ // optimization: no conversion for String values
+ if (String.class.equals(selectItemsType)) {
+ return null;
+ }
+
+ try {
+ converter = facesContext.getApplication().createConverter(selectItemsType);
+ } catch (FacesException e) {
+ // nothing - try again
+ }
}
+ }
+ return converter;
+ }
+ // #################################################################################################################
+ // ### END copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_SharedRendererUtils.java
+ // #################################################################################################################
+
+ // #################################################################################################################
+ // ### BEGIN copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_ComponentUtils.java
+ // #################################################################################################################
+ static String getPathToComponent(UIComponent component) {
+ StringBuffer buf = new StringBuffer();
+
+ if (component == null) {
+ buf.append("{Component-Path : ");
+ buf.append("[null]}");
+ return buf.toString();
+ }
- try {
- converter = facesContext.getApplication().createConverter(arrayComponentType);
- } catch (final FacesException e) {
- LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e);
- return submittedValue;
+ getPathToComponent(component, buf);
+
+ buf.insert(0, "{Component-Path : ");
+ buf.append("}");
+
+ return buf.toString();
+ }
+
+ private static void getPathToComponent(UIComponent component, StringBuffer buf) {
+ if (component == null) {
+ return;
+ }
+
+ StringBuffer intBuf = new StringBuffer();
+
+ intBuf.append("[Class: ");
+ intBuf.append(component.getClass().getName());
+ if (component instanceof UIViewRoot) {
+ intBuf.append(",ViewId: ");
+ intBuf.append(((UIViewRoot) component).getViewId());
+ } else {
+ intBuf.append(",Id: ");
+ intBuf.append(component.getId());
+ }
+ intBuf.append("]");
+
+ buf.insert(0, intBuf.toString());
+
+ getPathToComponent(component.getParent(), buf);
+ }
+ // #################################################################################################################
+ // ### END copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_ComponentUtils.java
+ // #################################################################################################################
+
+ // #################################################################################################################
+ // ### BEGIN copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_SelectItemsIterator.java
+ // #################################################################################################################
+ private static class SelectItemsIterator implements Iterator<SelectItem> {
+
+ private static final Iterator<UIComponent> EMPTY_UICOMPONENT_ITERATOR = new EmptyIterator<UIComponent>();
+
+ // org.apache.myfaces.shared.util.SelectItemsIterator uses JSFAttr
+ private static final String VAR_ATTR = "var";
+ private static final String ITEM_VALUE_ATTR = "itemValue";
+ private static final String ITEM_LABEL_ATTR = "itemLabel";
+ private static final String ITEM_DESCRIPTION_ATTR = "itemDescription";
+ private static final String ITEM_DISABLED_ATTR = "itemDisabled";
+ private static final String ITEM_LABEL_ESCAPED_ATTR = "itemLabelEscaped";
+ private static final String NO_SELECTION_VALUE_ATTR = "noSelectionValue";
+
+ private final Iterator<UIComponent> children;
+ private Iterator<?> nestedItems;
+ private SelectItem nextItem;
+ private UIComponent currentComponent;
+ private UISelectItems currentUISelectItems;
+ private FacesContext facesContext;
+
+ public SelectItemsIterator(UIComponent selectItemsParent, FacesContext facesContext) {
+ children = selectItemsParent.getChildCount() > 0
+ ? selectItemsParent.getChildren().iterator()
+ : EMPTY_UICOMPONENT_ITERATOR;
+ this.facesContext = facesContext;
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean hasNext() {
+ if (nextItem != null) {
+ return true;
+ }
+ if (nestedItems != null) {
+ if (nestedItems.hasNext()) {
+ return true;
+ }
+ nestedItems = null;
+ currentComponent = null;
}
+ if (children.hasNext()) {
+ UIComponent child = children.next();
+ // When there is other components nested that does
+ // not extends from UISelectItem or UISelectItems
+ // the behavior for this iterator is just skip this
+ // element(s) until an element that extends from these
+ // classes are found. If there is no more elements
+ // that conform this condition, just return false.
+ while (!(child instanceof UISelectItem) && !(child instanceof UISelectItems)) {
+ // Try to skip it
+ if (children.hasNext()) {
+ // Skip and do the same check
+ child = children.next();
+ } else {
+ // End loop, so the final result is return false,
+ // since there are no more components to iterate.
+ return false;
+ }
+ }
+ if (child instanceof UISelectItem) {
+ UISelectItem uiSelectItem = (UISelectItem) child;
+ Object item = uiSelectItem.getValue();
+ if (item == null) {
+ // no value attribute --> create the SelectItem out of the other attributes
+ Object itemValue = uiSelectItem.getItemValue();
+ String label = uiSelectItem.getItemLabel();
+ String description = uiSelectItem.getItemDescription();
+ boolean disabled = uiSelectItem.isItemDisabled();
+ boolean escape = uiSelectItem.isItemEscaped();
+ boolean noSelectionOption = uiSelectItem.isNoSelectionOption();
+ if (label == null) {
+ label = itemValue.toString();
+ }
+ item = new SelectItem(itemValue, label, description, disabled, escape, noSelectionOption);
+ } else if (!(item instanceof SelectItem)) {
+ ValueExpression expression = uiSelectItem.getValueExpression("value");
+ throw new IllegalArgumentException("ValueExpression '"
+ + (expression == null ? null : expression.getExpressionString()) + "' of UISelectItem : "
+ + getPathToComponent(child) + " does not reference an Object of type SelectItem");
+ }
+ nextItem = (SelectItem) item;
+ currentComponent = child;
+ return true;
+ } else if (child instanceof UISelectItems) {
+ currentUISelectItems = ((UISelectItems) child);
+ Object value = currentUISelectItems.getValue();
+ currentComponent = child;
+
+ if (value instanceof SelectItem) {
+ nextItem = (SelectItem) value;
+ return true;
+ } else if (value != null && value.getClass().isArray()) {
+ // value is any kind of array (primitive or non-primitive)
+ // --> we have to use class Array to get the values
+ int length = Array.getLength(value);
+ Collection<Object> items = new ArrayList<Object>(length);
+ for (int i = 0; i < length; i++) {
+ items.add(Array.get(value, i));
+ }
+ nestedItems = items.iterator();
+ return hasNext();
+ } else if (value instanceof Iterable) {
+ // value is Iterable --> Collection, DataModel,...
+ nestedItems = ((Iterable<?>) value).iterator();
+ return hasNext();
+ } else if (value instanceof Map) {
+ Map<Object, Object> map = ((Map<Object, Object>) value);
+ Collection<SelectItem> items = new ArrayList<SelectItem>(map.size());
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ items.add(new SelectItem(entry.getValue(), entry.getKey().toString()));
}
- // Now, we have a converter...
- // We determine the type of the component array after converting one of it's elements
- if (vb != null && arrayComponentType == null
- && valueType != null && valueType.isArray()) {
- if (submittedValue.length > 0) {
- arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass();
+ nestedItems = items.iterator();
+ return hasNext();
+ } else {
+
+ if ((facesContext.isProjectStage(ProjectStage.Production) && LOG.isDebugEnabled())
+ || LOG.isWarnEnabled()) {
+ ValueExpression expression = currentUISelectItems.getValueExpression("value");
+ Object[] objects = {
+ (expression == null ? null : expression.getExpressionString()),
+ getPathToComponent(child),
+ (value == null ? null : value.getClass().getName())
+ };
+ String message = "ValueExpression {0} of UISelectItems with component-path {1}"
+ + " does not reference an Object of type SelectItem,"
+ + " array, Iterable or Map, but of type: {2}";
+ if (facesContext.isProjectStage(ProjectStage.Production)) {
+ LOG.debug(message, objects);
+ } else {
+ LOG.warn(message, objects);
}
}
+ }
+ } else {
+ currentComponent = null;
+ }
+ }
+ return false;
+ }
- if (valueType == null) {
- // ...but have no idea of expected type
- // --> so let's convert it to an Object array
- final int len = submittedValue.length;
- final Object[] convertedValues = (Object[]) Array.newInstance(
- arrayComponentType == null ? Object.class : arrayComponentType, len);
- for (int i = 0; i < len; i++) {
- convertedValues[i]
- = converter.getAsObject(facesContext, component, submittedValue[i]);
- }
- return convertedValues;
- }
-
- if (List.class.isAssignableFrom(valueType)) {
- // Curious case: According to specs we should assume, that the element type
- // of this List is java.lang.String. But there is a Converter set for this
- // component. Because the user must know what he is doing, we will convert the values.
- final int length = submittedValue.length;
- final List<Object> list = new ArrayList<Object>(length);
- for (int i = 0; i < length; i++) {
- list.add(converter.getAsObject(facesContext, component, submittedValue[i]));
+ public SelectItem next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ if (nextItem != null) {
+ SelectItem value = nextItem;
+ nextItem = null;
+ return value;
+ }
+ if (nestedItems != null) {
+ Object item = nestedItems.next();
+
+ if (!(item instanceof SelectItem)) {
+ // check new params of SelectItems (since 2.0): itemValue, itemLabel, itemDescription,...
+ // Note that according to the spec UISelectItems does not provide Getter and Setter
+ // methods for this values, so we have to use the attribute map
+ Map<String, Object> attributeMap = currentUISelectItems.getAttributes();
+
+ // write the current item into the request map under the key listed in var, if available
+ boolean wroteRequestMapVarValue = false;
+ Object oldRequestMapVarValue = null;
+ String var = (String) attributeMap.get(VAR_ATTR);
+ if (var != null && !"".equals(var)) {
+ // save the current value of the key listed in var from the request map
+ oldRequestMapVarValue = facesContext.getExternalContext().getRequestMap().put(var, item);
+ wroteRequestMapVarValue = true;
+ }
+
+ // check the itemValue attribute
+ Object itemValue = attributeMap.get(ITEM_VALUE_ATTR);
+ if (itemValue == null) {
+ // the itemValue attribute was not provided
+ // --> use the current item as the itemValue
+ itemValue = item;
+ }
+
+ // Spec: When iterating over the select items, toString()
+ // must be called on the string rendered attribute values
+ Object itemLabel = attributeMap.get(ITEM_LABEL_ATTR);
+ if (itemLabel == null) {
+ itemLabel = itemValue.toString();
+ } else {
+ itemLabel = itemLabel.toString();
+ }
+ Object itemDescription = attributeMap.get(ITEM_DESCRIPTION_ATTR);
+ if (itemDescription != null) {
+ itemDescription = itemDescription.toString();
+ }
+ Boolean itemDisabled = getBooleanAttribute(currentUISelectItems, ITEM_DISABLED_ATTR, false);
+ Boolean itemLabelEscaped = getBooleanAttribute(currentUISelectItems, ITEM_LABEL_ESCAPED_ATTR, true);
+ Object noSelectionValue = attributeMap.get(NO_SELECTION_VALUE_ATTR);
+ item = new SelectItem(itemValue,
+ (String) itemLabel,
+ (String) itemDescription,
+ itemDisabled,
+ itemLabelEscaped,
+ itemValue.equals(noSelectionValue));
+
+ // remove the value with the key from var from the request map, if previously written
+ if (wroteRequestMapVarValue) {
+ // If there was a previous value stored with the key from var in the request map, restore it
+ if (oldRequestMapVarValue != null) {
+ facesContext.getExternalContext()
+ .getRequestMap().put(var, oldRequestMapVarValue);
+ } else {
+ facesContext.getExternalContext()
+ .getRequestMap().remove(var);
+ }
+ }
+ }
+ return (SelectItem) item;
}
- return list;
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
}
- if (arrayComponentType == null) {
- throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
+ public UIComponent getCurrentComponent() {
+ return currentComponent;
}
- if (arrayComponentType.isPrimitive()) {
- // primitive array
- final int len = submittedValue.length;
- final Object convertedValues = Array.newInstance(arrayComponentType, len);
- for (int i = 0; i < len; i++) {
- Array.set(convertedValues, i,
- converter.getAsObject(facesContext, component, submittedValue[i]));
+ private boolean getBooleanAttribute(UIComponent component, String attrName, boolean defaultValue) {
+ Object value = component.getAttributes().get(attrName);
+ if (value == null) {
+ return defaultValue;
+ } else if (value instanceof Boolean) {
+ return (Boolean) value;
+ } else {
+ // If the value is a String, parse the boolean.
+ // This makes the following code work: <tag attribute="true" />,
+ // otherwise you would have to write <tag attribute="#{true}" />.
+ return Boolean.valueOf(value.toString());
}
- return convertedValues;
- } else {
- // Object array
- final int length = submittedValue.length;
- final List<Object> convertedValues = new ArrayList<Object>(length);
- for (int i = 0; i < length; i++) {
- convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i]));
+ }
+
+ private String getPathToComponent(UIComponent component) {
+ StringBuffer buf = new StringBuffer();
+
+ if (component == null) {
+ buf.append("{Component-Path : ");
+ buf.append("[null]}");
+ return buf.toString();
}
- return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, length));
+
+ getPathToComponent(component, buf);
+
+ buf.insert(0, "{Component-Path : ");
+ buf.append("}");
+
+ return buf.toString();
+ }
+
+ private void getPathToComponent(UIComponent component, StringBuffer buf) {
+ if (component == null) {
+ return;
+ }
+
+ StringBuffer intBuf = new StringBuffer();
+
+ intBuf.append("[Class: ");
+ intBuf.append(component.getClass().getName());
+ if (component instanceof UIViewRoot) {
+ intBuf.append(",ViewId: ");
+ intBuf.append(((UIViewRoot) component).getViewId());
+ } else {
+ intBuf.append(",Id: ");
+ intBuf.append(component.getId());
+ }
+ intBuf.append("]");
+
+ buf.insert(0, intBuf);
+
+ getPathToComponent(component.getParent(), buf);
}
}
+ // #################################################################################################################
+ // ### END copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_SelectItemsIterator.java
+ // #################################################################################################################
+
+ // #################################################################################################################
+ // ### BEGIN copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_EmptyIterator.java
+ // #################################################################################################################
+ private static class EmptyIterator<T> implements Iterator<T> {
+ public boolean hasNext() {
+ return false;
+ }
+
+ public T next() {
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ // #################################################################################################################
+ // ### END copy out of https://svn.apache.org/repos/asf/myfaces/core/tags/myfaces-core-module-2.2.8/
+ // ### api/src/main/java/javax/faces/component/_EmptyIterator.java
+ // #################################################################################################################
}