You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2009/11/27 01:56:36 UTC

svn commit: r884730 - in /myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared: renderkit/RendererUtils.java renderkit/_SharedRendererUtils.java util/SelectItemsIterator.java

Author: lu4242
Date: Fri Nov 27 00:56:36 2009
New Revision: 884730

URL: http://svn.apache.org/viewvc?rev=884730&view=rev
Log:
MYFACES-2418 Implement h:selectManyXXX collectionType and hideNoSelectionOption (thanks to Jakob Korherr for this patch)

Modified:
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/RendererUtils.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/_SharedRendererUtils.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/util/SelectItemsIterator.java

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/RendererUtils.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/RendererUtils.java?rev=884730&r1=884729&r2=884730&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/RendererUtils.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/RendererUtils.java Fri Nov 27 00:56:36 2009
@@ -423,9 +423,8 @@
         ValueExpression ve = component.getValueExpression("value");
         if (ve == null) return null;
 
-        //By some strange reason ve.getType(facesContext.getELContext());
-        //does not return the same as ve.getValue(facesContext.getELContext()).getClass(),
-        //so we need to use this instead.
+        // Try to get the type from the actual value or,
+        // if value == null, obtain the type from the ValueExpression
         Class<?> valueType = null;
         Object value = ve.getValue(facesContext.getELContext()); 
         valueType = (value != null) 
@@ -434,21 +433,29 @@
         
         if (valueType == null) return null;
         
-        if (Collection.class.isAssignableFrom(valueType))
+        // a valueType of Object is also permitted, in order to support
+        // managed bean properties of type Object that resolve to null at this point
+        if (Collection.class.isAssignableFrom(valueType) || Object.class.equals(valueType))
         {
-            // Spec: do not convert the values
-            return null;
+            // try to get the by-type-converter from the type of the SelectItems
+            return _SharedRendererUtils.getSelectItemsValueConverter(new SelectItemsIterator(component, facesContext), facesContext);
         }
 
         if (!valueType.isArray())
         {
             throw new IllegalArgumentException("ValueExpression for UISelectMany : "
-                    + getPathToComponent(component) + " must be of type Collection or array");
+                    + getPathToComponent(component) + " must be of type Collection or Array");
         }
 
         Class<?> arrayComponentType = valueType.getComponentType();
         if (String.class.equals(arrayComponentType)) return null;    //No converter needed for String type
-        if (Object.class.equals(arrayComponentType)) return null;    //There is no converter for Object class
+        
+        if (Object.class.equals(arrayComponentType)) 
+        {    
+            // There is no converter for Object class
+            // try to get the by-type-converter from the type of the SelectItems
+            return _SharedRendererUtils.getSelectItemsValueConverter(new SelectItemsIterator(component, facesContext), facesContext);
+        }
 
         try
         {
@@ -460,8 +467,7 @@
             return null;
         }
     }
-
-
+    
     public static void checkParamValidity(FacesContext facesContext, UIComponent uiComponent, Class compClass)
     {
         if(facesContext == null)

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/_SharedRendererUtils.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/_SharedRendererUtils.java?rev=884730&r1=884729&r2=884730&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/_SharedRendererUtils.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/_SharedRendererUtils.java Fri Nov 27 00:56:36 2009
@@ -21,14 +21,15 @@
 import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Queue;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -39,6 +40,10 @@
 import javax.faces.context.FacesContext;
 import javax.faces.convert.Converter;
 import javax.faces.convert.ConverterException;
+import javax.faces.model.SelectItem;
+import javax.faces.model.SelectItemGroup;
+
+import org.apache.myfaces.shared.util.SelectItemsIterator;
 
 /**
  * The util methods in this class are shared between the javax.faces.component package and
@@ -87,9 +92,8 @@
         }
     }
 
-    static Object getConvertedUISelectManyValue(FacesContext facesContext,
-            UISelectMany component, String[] submittedValue)
-            throws ConverterException
+    static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,  
+                                                String[] submittedValue) throws ConverterException
     {
         // Attention!
         // This code is duplicated in jsfapi component package.
@@ -144,136 +148,159 @@
                 targetForConvertedValues = Array.newInstance(componentType,
                         submittedValue.length);
             }
-            else if (Collection.class.isAssignableFrom(modelType))
+            else if (Collection.class.isAssignableFrom(modelType) || Object.class.equals(modelType))
             {
-                // the target should be a Collection
-                Object collectionTypeAttr = component.getAttributes().get(
-                        COLLECTION_TYPE_KEY);
-                if (collectionTypeAttr != null)
+                if (converter == null)
                 {
-                    Class<?> collectionType = null;
-                    // if there is a value, it must be a ...
-                    // ... a ValueExpression that evaluates to a String or a Class
-                    if (collectionTypeAttr instanceof ValueExpression)
-                    {
-                        // get the value of the ValueExpression
-                        collectionTypeAttr = ((ValueExpression) collectionTypeAttr)
-                                .getValue(facesContext.getELContext());
-                    }
-                    // ... String that is a fully qualified Java class name
-                    if (collectionTypeAttr instanceof String)
+                    // try to get the by-type-converter from the type of the SelectItems
+                    SelectItemsIterator iterator = new SelectItemsIterator(component, facesContext);
+                    converter = getSelectItemsValueConverter(iterator, facesContext);
+                }
+                
+                if (Collection.class.isAssignableFrom(modelType))
+                {
+                    // the target should be a Collection
+                    Object collectionTypeAttr = component.getAttributes().get(
+                            COLLECTION_TYPE_KEY);
+                    if (collectionTypeAttr != null)
                     {
-                        try
+                        Class<?> collectionType = null;
+                        // if there is a value, it must be a ...
+                        // ... a ValueExpression that evaluates to a String or a Class
+                        if (collectionTypeAttr instanceof ValueExpression)
                         {
-                            collectionType = Class
-                                    .forName((String) collectionTypeAttr);
+                            // get the value of the ValueExpression
+                            collectionTypeAttr = ((ValueExpression) collectionTypeAttr)
+                                    .getValue(facesContext.getELContext());
                         }
-                        catch (ClassNotFoundException cnfe)
+                        // ... String that is a fully qualified Java class name
+                        if (collectionTypeAttr instanceof String)
+                        {
+                            try
+                            {
+                                collectionType = Class
+                                        .forName((String) collectionTypeAttr);
+                            }
+                            catch (ClassNotFoundException cnfe)
+                            {
+                                throw new FacesException(
+                                        "Unable to find class "
+                                                + collectionTypeAttr
+                                                + " on the classpath.", cnfe);
+                            }
+    
+                        }
+                        // ... a Class object
+                        else if (collectionTypeAttr instanceof Class)
+                        {
+                            collectionType = (Class<?>) collectionTypeAttr;
+                        }
+                        else
                         {
                             throw new FacesException(
-                                    "Unable to find class "
-                                            + collectionTypeAttr
-                                            + " on the classpath.", cnfe);
+                                    "The attribute "
+                                            + COLLECTION_TYPE_KEY
+                                            + " of component "
+                                            + component.getClientId()
+                                            + " does not evaluate to a "
+                                            + "String, a Class object or a ValueExpression pointing "
+                                            + "to a String or a Class object.");
                         }
-
-                    }
-                    // ... a Class object
-                    else if (collectionTypeAttr instanceof Class)
-                    {
-                        collectionType = (Class<?>) collectionTypeAttr;
-                    }
-                    else
-                    {
-                        throw new FacesException(
-                                "The attribute "
-                                        + COLLECTION_TYPE_KEY
-                                        + " of component "
-                                        + component.getClientId()
-                                        + " 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()
-                                + " 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
-                {
-                    // 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
+                        // 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()
+                                    + " does not point to a valid type of Collection.");
+                        }
+                        // now we have a real collectionType --> try to instantiate it
                         try
                         {
-                            Method cloneMethod = componentValue.getClass()
-                                    .getMethod("clone");
-                            Collection<?> clone = (Collection<?>) cloneMethod
-                                    .invoke(componentValue);
-                            clone.clear();
-                            targetForConvertedValues = clone;
+                            targetForConvertedValues = collectionType.newInstance();
                         }
                         catch (Exception e)
                         {
-                            log(facesContext, "Could not clone "
-                                    + componentValue.getClass().getName(), e);
+                            throw new FacesException("The Collection "
+                                    + collectionType.getName()
+                                    + "can not be instantiated.", e);
                         }
                     }
-
-                    // if clone did not work
-                    if (targetForConvertedValues == null)
+                    else
                     {
-                        // try to create the (concrete) collection from modelType 
-                        // or with the class object of componentValue (if any)
-                        try
+                        // component.getValue() will implement Collection at this point
+                        Collection<?> componentValue = (Collection<?>) component
+                                .getValue();
+                        // can we clone the Collection
+                        if (componentValue instanceof Cloneable)
                         {
-                            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))
+                            // clone method of Object is protected --> use reflection
+                            try
                             {
-                                targetForConvertedValues = new TreeSet();
+                                Method cloneMethod = componentValue.getClass()
+                                        .getMethod("clone");
+                                Collection<?> clone = (Collection<?>) cloneMethod
+                                        .invoke(componentValue);
+                                clone.clear();
+                                targetForConvertedValues = clone;
                             }
-                            else if (Queue.class.isAssignableFrom(modelType))
+                            catch (Exception e)
                             {
-                                targetForConvertedValues = new LinkedList();
+                                log(facesContext, "Could not clone "
+                                        + componentValue.getClass().getName(), e);
                             }
-                            else if (Set.class.isAssignableFrom(modelType))
+                        }
+    
+                        // 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 = new HashSet(
-                                        submittedValue.length);
+                                targetForConvertedValues = (componentValue != null ? componentValue
+                                        .getClass()
+                                        : modelType).newInstance();
                             }
-                            else
+                            catch (Exception e)
                             {
-                                targetForConvertedValues = new ArrayList(
-                                        submittedValue.length);
+                                // 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;
+                    }
+                    
+                    targetForConvertedValues = new Object[submittedValue.length];
+                }
             }
             else
             {
@@ -316,6 +343,51 @@
 
         return targetForConvertedValues;
     }
+    
+    /**
+     * 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;
+    }
 
     //private static final Log log = LogFactory.getLog(_SharedRendererUtils.class);
     private static final Logger log = Logger.getLogger(_SharedRendererUtils.class.getName());

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/util/SelectItemsIterator.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/util/SelectItemsIterator.java?rev=884730&r1=884729&r2=884730&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/util/SelectItemsIterator.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/util/SelectItemsIterator.java Fri Nov 27 00:56:36 2009
@@ -58,7 +58,6 @@
     private final Iterator<UIComponent> _children;
     private Iterator<? extends Object> _nestedItems;
     private SelectItem _nextItem;
-    private String _collectionLabel;
     private UISelectItems _currentUISelectItems;
     private FacesContext _facesContext;
 
@@ -157,22 +156,12 @@
                         items.add(Array.get(value, i));
                     }
                     _nestedItems = items.iterator();
-                    _collectionLabel = "Array";
                     return hasNext();
                 }
                 else if (value instanceof Iterable)
                 {
                     // value is Iterable --> Collection, DataModel,...
                     _nestedItems = ((Iterable<?>) value).iterator();
-                    // For better understanding ask if value is a Collection
-                    if (value instanceof Collection)
-                    {
-                        _collectionLabel = "Collection";
-                    }
-                    else
-                    {
-                        _collectionLabel = "Iterable";
-                    }
                     return hasNext();
                 }
                 else if (value instanceof Map)
@@ -185,7 +174,6 @@
                     }
                     
                     _nestedItems = items.iterator();
-                    _collectionLabel = "Map";
                     return hasNext();
                 }
                 else
@@ -237,66 +225,57 @@
                     oldRequestMapVarValue = _facesContext.getExternalContext().getRequestMap().put(var, item);
                     wroteRequestMapVarValue = true;
                 }
-                try
+                
+                // 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
                 {
-                    Object itemValue = attributeMap.get(ITEM_VALUE_ATTR);
-                    if(itemValue != null) 
+                    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)
                     {
-                        // 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.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)); 
+                        _facesContext.getExternalContext()
+                                .getRequestMap().put(var, oldRequestMapVarValue);
                     }
-                    else 
+                    else
                     {
-                        ValueExpression expression = _currentUISelectItems.getValueExpression("value");
-                        throw new IllegalArgumentException(
-                                _collectionLabel + " referenced by UISelectItems with ValueExpression '"
-                                + expression.getExpressionString()
-                                + "' and Component-Path : " + RendererUtils.getPathToComponent(_currentUISelectItems)
-                                + " does not contain Objects of type SelectItem"
-                                + " or does not provide the attribute itemValue");
+                        _facesContext.getExternalContext()
+                                .getRequestMap().remove(var);
                     }
-                }
-                finally
-                {
-                    // 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;
         }