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/04 20:58:14 UTC

svn commit: r832865 - in /myfaces: core/trunk/api/src/main/java/javax/faces/component/ core/trunk/api/src/test/java/javax/faces/component/ shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/ shared/trunk_4.0.x/core/src/main/java/...

Author: lu4242
Date: Wed Nov  4 19:58:13 2009
New Revision: 832865

URL: http://svn.apache.org/viewvc?rev=832865&view=rev
Log:
MYFACES-2309 Add new attributes to f:selectItems (Thanks to Jakob Korherr for this patch)

Modified:
    myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectMany.java
    myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectOne.java
    myfaces/core/trunk/api/src/main/java/javax/faces/component/_SelectItemsIterator.java
    myfaces/core/trunk/api/src/main/java/javax/faces/component/_UISelectItems.java
    myfaces/core/trunk/api/src/test/java/javax/faces/component/UISelectItemsTest.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java
    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/html/HtmlCheckboxRendererBase.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRadioRendererBase.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/util/SelectItemsIterator.java

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectMany.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectMany.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectMany.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectMany.java Wed Nov  4 19:58:13 2009
@@ -373,7 +373,7 @@
             };
 
             Collection<SelectItem> items = new ArrayList<SelectItem>();
-            for (Iterator<SelectItem> iter = new _SelectItemsIterator(this); iter.hasNext();)
+            for (Iterator<SelectItem> iter = new _SelectItemsIterator(this, context); iter.hasNext();)
             {
                 items.add(iter.next());
             }

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectOne.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectOne.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectOne.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/UISelectOne.java Wed Nov  4 19:58:13 2009
@@ -87,10 +87,10 @@
 
         // selected value must match to one of the available options
         // and if required is true it must not match an option with noSelectionOption set to true (since 2.0)
-        if (!(_SelectItemsUtil.matchValue(context, value, new _SelectItemsIterator(this), converter)
+        if (!(_SelectItemsUtil.matchValue(context, value, new _SelectItemsIterator(this, context), converter)
               && (!this.isRequired() 
                   || (this.isRequired() 
-                      && !_SelectItemsUtil.isNoSelectionOption(context, value, new _SelectItemsIterator(this), converter)))))
+                      && !_SelectItemsUtil.isNoSelectionOption(context, value, new _SelectItemsIterator(this, context), converter)))))
         {
             _MessageUtils.addErrorMessage(context, this, INVALID_MESSAGE_ID, new Object[] { _MessageUtils.getLabel(
                 context, this) });

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/_SelectItemsIterator.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/_SelectItemsIterator.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/_SelectItemsIterator.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/_SelectItemsIterator.java Wed Nov  4 19:58:13 2009
@@ -18,28 +18,49 @@
  */
 package javax.faces.component;
 
-import java.util.*;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
 
 import javax.el.ValueExpression;
 import javax.faces.context.FacesContext;
-import javax.faces.el.ValueBinding;
 import javax.faces.model.SelectItem;
 
+// ATTENTION
+// This class is associated with org.apache.myfaces.shared.util.SelectItemsIterator.
+// Changes here should also be applied to this class.
+
 /**
  * @author Mathias Broekelmann (latest modification by $Author$)
+ * @author Jakob Korherr (jsf 2.0)
  * @version $Revision$ $Date$
  */
 class _SelectItemsIterator implements Iterator<SelectItem>
 {
-    private final Iterator<UIComponent> _childs;
-    private Iterator<SelectItem> _nestedItems;
+    
+    // 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<? extends Object> _nestedItems;
     private SelectItem _nextItem;
     private String _collectionLabel;
     private UISelectItems _currentUISelectItems;
+    private FacesContext _facesContext;
 
-    public _SelectItemsIterator(UIComponent selectItemsParent)
+    public _SelectItemsIterator(UIComponent selectItemsParent, FacesContext facesContext)
     {
-        _childs = selectItemsParent.getChildren().iterator();
+        _children = selectItemsParent.getChildren().iterator();
+        _facesContext = facesContext;
     }
 
     @SuppressWarnings("unchecked")
@@ -55,17 +76,11 @@
             {
                 return true;
             }
-            // remove the last value from the request map
-            if(_currentUISelectItems.getVar() != null && !"".equals(_currentUISelectItems.getVar()))
-            {
-                FacesContext.getCurrentInstance().getExternalContext()
-                    .getRequestMap().remove(_currentUISelectItems.getVar());
-            } 
             _nestedItems = null;
         }
-        if (_childs.hasNext())
+        if (_children.hasNext())
         {
-            UIComponent child = _childs.next();
+            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
@@ -75,10 +90,10 @@
             while (!(child instanceof UISelectItem) && !(child instanceof UISelectItems))
             {
                 // Try to skip it
-                if (_childs.hasNext())
+                if (_children.hasNext())
                 {
                     // Skip and do the same check
-                    child = _childs.next();
+                    child = _children.next();
                 }
                 else
                 {
@@ -93,24 +108,27 @@
                 Object item = uiSelectItem.getValue();
                 if (item == null)
                 {
-                    Object itemValue = ((UISelectItem) child).getItemValue();
-                    String label = ((UISelectItem) child).getItemLabel();
-                    String description = ((UISelectItem) child).getItemDescription();
-                    boolean disabled = ((UISelectItem) child).isItemDisabled();
+                    // 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);
+                    item = new SelectItem(itemValue, label, description, disabled, escape, noSelectionOption);
                 }
                 else if (!(item instanceof SelectItem))
                 {
-                    ValueBinding binding = ((UISelectItem) child).getValueBinding("value");
-                    throw new IllegalArgumentException("Value binding '"
-                            + (binding == null ? null : binding.getExpressionString()) + "' of UISelectItem : "
+                    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;
+                _nextItem = (SelectItem) item;
                 return true;
             }
             else if (child instanceof UISelectItems)
@@ -120,19 +138,36 @@
 
                 if (value instanceof SelectItem)
                 {
-                    _nextItem = (SelectItem)value;
+                    _nextItem = (SelectItem) value;
                     return true;
                 }
-                else if (value instanceof SelectItem[])
+                else if (value != null && value.getClass().isArray())
                 {
-                    _nestedItems = Arrays.asList((SelectItem[]) value).iterator();
+                    // value is any kind of array (primitive or non-primitive)
+                    // --> we have to use class Array to get the values
+                    final 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();
                     _collectionLabel = "Array";
                     return hasNext();
                 }
-                else if (value instanceof Collection)
+                else if (value instanceof Iterable)
                 {
-                    _nestedItems = ((Collection<SelectItem>) value).iterator();
-                    _collectionLabel = "Collection";
+                    // 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)
@@ -150,15 +185,14 @@
                 }
                 else
                 {
-                    ValueBinding binding = _currentUISelectItems.getValueBinding("value");
-
+                    ValueExpression expression = _currentUISelectItems.getValueExpression("value");
                     throw new IllegalArgumentException(
-                        "Value binding '"
-                                + (binding == null ? null : binding.getExpressionString())
-                                + "'of UISelectItems with component-path "
-                                + getPathToComponent(child)
-                                + " does not reference an Object of type SelectItem, SelectItem[], Collection or Map but of type : "
-                                + ((value == null) ? null : value.getClass().getName()));
+                            "ValueExpression '"
+                            + (expression == null ? null : expression.getExpressionString())
+                            + "'of UISelectItems with component-path "
+                            + getPathToComponent(child)
+                            + " does not reference an Object of type SelectItem, array, Iterable or Map, but of type : "
+                            + ((value == null) ? null : value.getClass().getName()));
                 }
             }
         }
@@ -181,39 +215,82 @@
         {
             Object item = _nestedItems.next();
             
-            // write the current item into the request map under the key listed in var, if available
-            if(_currentUISelectItems.getVar() != null && !"".equals(_currentUISelectItems.getVar()))
-            {
-                FacesContext.getCurrentInstance().getExternalContext()
-                    .getRequestMap().put(_currentUISelectItems.getVar(), item);
-            }
-            
             if (!(item instanceof SelectItem))
             {
-                // check new params of SelectItems (since 2.0) itemValue, itemLabel, itemDescription,...
-                Object itemValue = _currentUISelectItems.getItemValue();
-                if(itemValue != null) 
-                {
-                    String itemLabel = _currentUISelectItems.getItemLabel() == null ?
-                            itemValue.toString() : 
-                            _currentUISelectItems.getItemLabel();
-                    item = new SelectItem(itemValue,
-                        itemLabel,
-                        _currentUISelectItems.getItemDescription(),
-                        _currentUISelectItems.isItemDisabled(),
-                        _currentUISelectItems.isItemLabelEscaped(),
-                        itemValue.equals(_currentUISelectItems.getNoSelectionValue())
-                            || itemLabel.equals(_currentUISelectItems.getNoSelectionValue())); 
+                // 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;
+                final 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;
                 }
-                else 
+                try
                 {
-                    ValueExpression expression = _currentUISelectItems.getValueExpression("value");
-                    throw new IllegalArgumentException(
-                        _collectionLabel + " referenced by UISelectItems with binding '"
-                        + expression.getExpressionString()
-                        + "' and Component-Path : " + getPathToComponent(_currentUISelectItems)
-                        + " does not contain Objects of type SelectItem"
-                        + " or does not provide the attribute itemValue");
+                    Object itemValue = attributeMap.get(ITEM_VALUE_ATTR);
+                    if(itemValue != 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)); 
+                    }
+                    else 
+                    {
+                        ValueExpression expression = _currentUISelectItems.getValueExpression("value");
+                        throw new IllegalArgumentException(
+                                _collectionLabel + " referenced by UISelectItems with ValueExpression '"
+                                + expression.getExpressionString()
+                                + "' and Component-Path : " + getPathToComponent(_currentUISelectItems)
+                                + " does not contain Objects of type SelectItem"
+                                + " or does not provide the attribute itemValue");
+                    }
+                }
+                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;
@@ -225,6 +302,26 @@
     {
         throw new UnsupportedOperationException();
     }
+    
+    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());
+        }
+    }
 
     private String getPathToComponent(UIComponent component)
     {

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/_UISelectItems.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/_UISelectItems.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/_UISelectItems.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/_UISelectItems.java Wed Nov  4 19:58:13 2009
@@ -19,6 +19,7 @@
 package javax.faces.component;
 
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFExclude;
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
 
 /**
@@ -79,8 +80,11 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty(literalOnly = true)
-  public abstract String getVar();
+  public String getVar() {
+      return null;
+  }
   
   /**
    * The value for the current item.
@@ -88,8 +92,11 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty
-  public abstract Object getItemValue();
+  public Object getItemValue() {
+      return null;
+  }
   
   /**
    * The label of the current item.
@@ -97,8 +104,11 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty
-  public abstract String getItemLabel();
+  public String getItemLabel() {
+      return null;
+  }
   
   /**
    * The description of the current item.
@@ -106,8 +116,11 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty
-  public abstract String getItemDescription();
+  public String getItemDescription() {
+      return null;
+  }
   
   /**
    * Determines if the current item is selectable or not.
@@ -115,8 +128,11 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty(defaultValue = "false")
-  public abstract boolean isItemDisabled();
+  public boolean isItemDisabled() {
+      return false;
+  }
   
   /**
    * Determines if the rendered markup for the current item receives
@@ -125,8 +141,11 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty(defaultValue = "true")
-  public abstract boolean isItemLabelEscaped();
+  public boolean isItemLabelEscaped() {
+      return true;
+  }
   
   /**
    * Is either an EL expression pointing to the element in the value collection
@@ -138,7 +157,10 @@
    * @since 2.0
    * @return
    */
+  @JSFExclude
   @JSFProperty
-  public abstract Object getNoSelectionValue();
+  public Object getNoSelectionValue() {
+      return null;
+  }
 
 }

Modified: myfaces/core/trunk/api/src/test/java/javax/faces/component/UISelectItemsTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/test/java/javax/faces/component/UISelectItemsTest.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/test/java/javax/faces/component/UISelectItemsTest.java (original)
+++ myfaces/core/trunk/api/src/test/java/javax/faces/component/UISelectItemsTest.java Wed Nov  4 19:58:13 2009
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 package javax.faces.component;
 
 import java.util.ArrayList;
@@ -8,6 +26,11 @@
 import org.apache.shale.test.base.AbstractJsfTestCase;
 import org.apache.shale.test.el.MockValueExpression;
 
+/**
+ * Tests for UISelectItems.
+ * @author Jakob Korherr (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
 public class UISelectItemsTest extends AbstractJsfTestCase
 {
 
@@ -15,7 +38,7 @@
     {
         super(name);
     }
-    
+
     public void testStringListAsValue() 
     {
         List<String> value = new ArrayList<String>();
@@ -25,14 +48,14 @@
         
         UISelectItems selectItems = new UISelectItems();
         selectItems.setValue(value);
-        selectItems.setVar("item");
+        selectItems.getAttributes().put("var", "item");
         ValueExpression itemValue = new MockValueExpression("#{item}", Object.class);
         selectItems.setValueExpression("itemValue" , itemValue);
         
         UISelectOne selectOne = new UISelectOne();
         selectOne.getChildren().add(selectItems);
         
-        _SelectItemsIterator iter = new _SelectItemsIterator(selectOne);
+        _SelectItemsIterator iter = new _SelectItemsIterator(selectOne, facesContext);
         List<String> options = new ArrayList<String>();
         while(iter.hasNext())
         {
@@ -41,4 +64,31 @@
         
         assertEquals(value, options);
     }
+    
+    public void testPrimitiveArrayAsValue()
+    {
+        int[] value = new int[3];
+        value[0] = 1;
+        value[1] = 2;
+        value[2] = 3;
+        
+        UISelectItems selectItems = new UISelectItems();
+        selectItems.setValue(value);
+        selectItems.getAttributes().put("var", "item");
+        ValueExpression itemValue = new MockValueExpression("#{item}", Object.class);
+        selectItems.setValueExpression("itemValue" , itemValue);
+        
+        UISelectOne selectOne = new UISelectOne();
+        selectOne.getChildren().add(selectItems);
+        
+        _SelectItemsIterator iter = new _SelectItemsIterator(selectOne, facesContext);
+        int[] options = new int[3];
+        for (int i = 0; i < 3; i++)
+        {
+            options[i] = (Integer) iter.next().getValue();
+            
+            // test equality
+            assertEquals(value[i], options[i]);
+        }
+    }
 }

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java Wed Nov  4 19:58:13 2009
@@ -127,6 +127,11 @@
     String ITEM_LABEL_ATTR             = "itemLabel";
     String ITEM_VALUE_ATTR             = "itemValue";
     String ITEM_ESCAPED_ATTR           = "itemEscaped";
+    String NO_SELECTION_OPTION_ATTR    = "noSelectionOption";
+    
+    // UISelectItems attributes
+    String ITEM_LABEL_ESCAPED_ATTR     = "itemLabelEscaped";
+    String NO_SELECTION_VALUE_ATTR     = "noSelectionValue";
 
     // UIData attributes
     String ROWS_ATTR                   = "rows";

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=832865&r1=832864&r2=832865&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 Wed Nov  4 19:58:13 2009
@@ -441,7 +441,7 @@
             // contains by looking at the SelectItem value of the first item. With generics in
             // JDK 1.5, it would be much easier to determine the type.
 
-            List selectItems = RendererUtils.internalGetSelectItemList(component);
+            List selectItems = RendererUtils.internalGetSelectItemList(component, facesContext);
 
             if (selectItems != null && selectItems.size() > 0)
             {
@@ -539,23 +539,25 @@
 
     /**
      * @param uiSelectOne
+     * @param facesContext
      * @return List of SelectItem Objects
      */
-    public static List getSelectItemList(UISelectOne uiSelectOne)
+    public static List getSelectItemList(UISelectOne uiSelectOne, FacesContext facesContext)
     {
-        return internalGetSelectItemList(uiSelectOne);
+        return internalGetSelectItemList(uiSelectOne, facesContext);
     }
 
     /**
      * @param uiSelectMany
+     * @param facesContext
      * @return List of SelectItem Objects
      */
-    public static List getSelectItemList(UISelectMany uiSelectMany)
+    public static List getSelectItemList(UISelectMany uiSelectMany, FacesContext facesContext)
     {
-        return internalGetSelectItemList(uiSelectMany);
+        return internalGetSelectItemList(uiSelectMany, facesContext);
     }
 
-    private static List internalGetSelectItemList(UIComponent uiComponent)
+    private static List internalGetSelectItemList(UIComponent uiComponent, FacesContext facesContext)
     {
         /* TODO: Shall we cache the list in a component attribute?
         ArrayList list = (ArrayList)uiComponent.getAttributes().get(SELECT_ITEM_LIST_ATTR);
@@ -567,7 +569,7 @@
         
         List list = new ArrayList();
         
-        for (Iterator iter = new SelectItemsIterator(uiComponent); iter.hasNext();)
+        for (Iterator iter = new SelectItemsIterator(uiComponent, facesContext); iter.hasNext();)
         {
             list.add(iter.next());            
         }        

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlCheckboxRendererBase.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlCheckboxRendererBase.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlCheckboxRendererBase.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlCheckboxRendererBase.java Wed Nov  4 19:58:13 2009
@@ -122,7 +122,7 @@
 
         int itemNum = 0;
 
-        for (Iterator it = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(selectMany)
+        for (Iterator it = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(selectMany, facesContext)
                 .iterator(); it.hasNext();) {
             SelectItem selectItem = (SelectItem) it.next();
             

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRadioRendererBase.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRadioRendererBase.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRadioRendererBase.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRadioRendererBase.java Wed Nov  4 19:58:13 2009
@@ -91,7 +91,7 @@
         if (!pageDirectionLayout) writer.startElement(HTML.TR_ELEM, selectOne);
 
         Converter converter;
-        List selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(selectOne);
+        List selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(selectOne, facesContext);
         converter = HtmlRendererUtils.findUIOutputConverterFailSafe(facesContext, selectOne);
 
         Object currentValue = org.apache.myfaces.shared.renderkit.RendererUtils.getObjectValue(selectOne);

Modified: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java?rev=832865&r1=832864&r2=832865&view=diff
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java (original)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java Wed Nov  4 19:58:13 2009
@@ -394,11 +394,11 @@
         if (selectMany) {
             writer.writeAttribute(HTML.MULTIPLE_ATTR, HTML.MULTIPLE_ATTR, null);
             selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils
-                    .getSelectItemList((UISelectMany) uiComponent);
+                    .getSelectItemList((UISelectMany) uiComponent, facesContext);
             converter = findUISelectManyConverterFailsafe(facesContext, uiComponent);
         } else {
             selectItemList = RendererUtils
-                    .getSelectItemList((UISelectOne) uiComponent);
+                    .getSelectItemList((UISelectOne) uiComponent, facesContext);
             converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
         }
 
@@ -811,12 +811,12 @@
             if (uiComponent instanceof UISelectMany) {
                 isSelectOne = false;
                 selectItemList = RendererUtils
-                        .getSelectItemList((UISelectMany) uiComponent);
+                        .getSelectItemList((UISelectMany) uiComponent, facesContext);
                 converter = findUISelectManyConverterFailsafe(facesContext, uiComponent);
             } else if(uiComponent instanceof UISelectOne){
                 isSelectOne = true;
                 selectItemList = RendererUtils
-                        .getSelectItemList((UISelectOne) uiComponent);
+                        .getSelectItemList((UISelectOne) uiComponent, facesContext);
                 converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
             }
 

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=832865&r1=832864&r2=832865&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 Wed Nov  4 19:58:13 2009
@@ -18,8 +18,8 @@
  */
 package org.apache.myfaces.shared.util;
 
+import java.lang.reflect.Array;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map;
@@ -30,70 +30,80 @@
 import javax.faces.component.UISelectItem;
 import javax.faces.component.UISelectItems;
 import javax.faces.context.FacesContext;
-import javax.faces.el.ValueBinding;
 import javax.faces.model.SelectItem;
 
+import org.apache.myfaces.shared.renderkit.JSFAttr;
 import org.apache.myfaces.shared.renderkit.RendererUtils;
 
+// ATTENTION
+// This class is associated with javax.faces.component._SelectItemsIterator.
+// Changes here should also be applied to this class.
+
 /**
  * @author Mathias Broekelmann (latest modification by $Author$)
+ * @author Jakob Korherr (jsf 2.0)
  * @version $Revision$ $Date$
  */
-public class SelectItemsIterator implements Iterator
+public class SelectItemsIterator implements Iterator<SelectItem>
 {
-    private final Iterator _childs;
-    private Iterator _nestedItems;
-    private Object _nextItem;
+    
+    private static final String VAR_ATTR = JSFAttr.VAR_ATTR;
+    private static final String ITEM_VALUE_ATTR = JSFAttr.ITEM_VALUE_ATTR;
+    private static final String ITEM_LABEL_ATTR = JSFAttr.ITEM_LABEL_ATTR;
+    private static final String ITEM_DESCRIPTION_ATTR = JSFAttr.ITEM_DESCRIPTION_ATTR;
+    private static final String ITEM_DISABLED_ATTR = JSFAttr.ITEM_DISABLED_ATTR;
+    private static final String ITEM_LABEL_ESCAPED_ATTR = JSFAttr.ITEM_LABEL_ESCAPED_ATTR;
+    private static final String NO_SELECTION_VALUE_ATTR = JSFAttr.NO_SELECTION_VALUE_ATTR;
+    
+    private final Iterator<UIComponent> _children;
+    private Iterator<? extends Object> _nestedItems;
+    private SelectItem _nextItem;
     private String _collectionLabel;
     private UISelectItems _currentUISelectItems;
+    private FacesContext _facesContext;
 
-    public SelectItemsIterator(UIComponent selectItemsParent)
+    public SelectItemsIterator(UIComponent selectItemsParent, FacesContext facesContext)
     {
-        _childs = selectItemsParent.getChildren().iterator();
+        _children = selectItemsParent.getChildren().iterator();
+        _facesContext = facesContext;
     }
 
+    @SuppressWarnings("unchecked")
     public boolean hasNext()
     {
-        if(_nextItem != null)
+        if (_nextItem != null)
         {
             return true;
         }
-        if(_nestedItems != null)
+        if (_nestedItems != null)
         {
-            if(_nestedItems.hasNext())
+            if (_nestedItems.hasNext())
             {
                 return true;
             }
-            // remove the last value from the request map
-            if(_currentUISelectItems.getVar() != null && !"".equals(_currentUISelectItems.getVar()))
-            {
-                FacesContext.getCurrentInstance().getExternalContext()
-                    .getRequestMap().remove(_currentUISelectItems.getVar());   
-            } 
             _nestedItems = null;
-        }            
-        if (_childs.hasNext())
+        }
+        if (_children.hasNext())
         {
-            UIComponent child = (UIComponent) _childs.next();
+            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))
+            while (!(child instanceof UISelectItem) && !(child instanceof UISelectItems))
             {
-                //Try to skip it
-                if (_childs.hasNext())
+                // Try to skip it
+                if (_children.hasNext())
                 {
-                    //Skip and do the same check
-                    child = (UIComponent) _childs.next();
+                    // 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.
+                    // End loop, so the final result is return false,
+                    // since there are no more components to iterate.
                     return false;
                 }
             }
@@ -103,31 +113,27 @@
                 Object item = uiSelectItem.getValue();
                 if (item == null)
                 {
-                    Object itemValue = ((UISelectItem) child).getItemValue();
-                    String label = ((UISelectItem) child).getItemLabel();
-                    String description = ((UISelectItem) child)
-                                    .getItemDescription();
-                    boolean disabled = ((UISelectItem) child).isItemDisabled();
-                    boolean escaped = ((UISelectItem) child).isItemEscaped();
+                    // 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, escaped);
+                    item = new SelectItem(itemValue, label, description, disabled, escape, noSelectionOption);
                 }
                 else if (!(item instanceof SelectItem))
                 {
-                    ValueBinding binding = ((UISelectItem) child)
-                                    .getValueBinding("value");
-                    throw new IllegalArgumentException(
-                                    "Value binding '"
-                                    + (binding == null ? null : binding.getExpressionString())
-                                    + "' of UISelectItem : "
-                                    + RendererUtils.getPathToComponent(child)
-                                    + " does not reference an Object of type SelectItem");
+                    ValueExpression expression = uiSelectItem.getValueExpression("value");
+                    throw new IllegalArgumentException("ValueExpression '"
+                            + (expression == null ? null : expression.getExpressionString()) + "' of UISelectItem : "
+                            + RendererUtils.getPathToComponent(child) + " does not reference an Object of type SelectItem");
                 }
-                _nextItem = item;
+                _nextItem = (SelectItem) item;
                 return true;
             }
             else if (child instanceof UISelectItems)
@@ -137,109 +143,162 @@
 
                 if (value instanceof SelectItem)
                 {
-                    _nextItem = value;
+                    _nextItem = (SelectItem) value;
                     return true;
                 }
-                else if (value instanceof SelectItem[])
+                else if (value != null && value.getClass().isArray())
                 {
-                    _nestedItems = Arrays.asList((SelectItem[]) value)
-                                    .iterator();
+                    // value is any kind of array (primitive or non-primitive)
+                    // --> we have to use class Array to get the values
+                    final 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();
                     _collectionLabel = "Array";
                     return hasNext();
                 }
-                else if (value instanceof Collection)
+                else if (value instanceof Iterable)
                 {
-                    _nestedItems = ((Collection)value).iterator();
-                    _collectionLabel = "Collection";
+                    // 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)
                 {
-                    Map map = ((Map) value);
-                    Collection items = new ArrayList(map.size()); 
-                    for (Iterator it = map.entrySet().iterator(); it
-                                    .hasNext();)
-                    {
-                        Map.Entry entry = (Map.Entry) it.next();
-                        items.add(new SelectItem(entry.getValue(), entry
-                                        .getKey().toString()));
+                    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()));
                     }
+                    
                     _nestedItems = items.iterator();
                     _collectionLabel = "Map";
                     return hasNext();
                 }
                 else
                 {
-                    ValueBinding binding = _currentUISelectItems.getValueBinding("value");
-
+                    ValueExpression expression = _currentUISelectItems.getValueExpression("value");
                     throw new IllegalArgumentException(
-                        "Value binding '"
-                        + (binding == null ? null : binding
-                                        .getExpressionString())
-                        + "'of UISelectItems with component-path "
-                        + RendererUtils.getPathToComponent(child)
-                        + " does not reference an Object of type SelectItem, SelectItem[], Collection or Map but of type : "
-                        + ((value == null) ? null : value
-                                        .getClass()
-                                        .getName()));
+                            "ValueExpression '"
+                            + (expression == null ? null : expression.getExpressionString())
+                            + "'of UISelectItems with component-path "
+                            + RendererUtils.getPathToComponent(child)
+                            + " does not reference an Object of type SelectItem, array, Iterable or Map, but of type : "
+                            + ((value == null) ? null : value.getClass().getName()));
                 }
             }
         }
         return false;
     }
 
-    public Object next()
+    public SelectItem next()
     {
         if (!hasNext())
         {
             throw new NoSuchElementException();
         }
-        if(_nextItem != null)
+        if (_nextItem != null)
         {
-            Object value = _nextItem;
+            SelectItem value = _nextItem;
             _nextItem = null;
             return value;
-        }        
+        }
         if (_nestedItems != null)
         {
             Object item = _nestedItems.next();
             
-            // write the current item into the request map under the key listed in var, if available
-            if(_currentUISelectItems.getVar() != null && !"".equals(_currentUISelectItems.getVar()))
-            {
-                FacesContext.getCurrentInstance().getExternalContext()
-                    .getRequestMap().put(_currentUISelectItems.getVar(), item);   
-            }
-            
             if (!(item instanceof SelectItem))
             {
-                // check new params of SelectItems (since 2.0) itemValue, itemLabel,...
-                Object itemValue = _currentUISelectItems.getItemValue();
-                if(itemValue != null) 
-                {
-                    String itemLabel = _currentUISelectItems.getItemLabel() == null ?
-                                itemValue.toString() : 
-                                _currentUISelectItems.getItemLabel();
-                    item = new SelectItem(itemValue,
-                            itemLabel,
-                            _currentUISelectItems.getItemDescription(),
-                            _currentUISelectItems.isItemDisabled(),
-                            _currentUISelectItems.isItemLabelEscaped(),
-                            itemValue.equals(_currentUISelectItems.getNoSelectionValue())
-                                || itemLabel.equals(_currentUISelectItems.getNoSelectionValue()));
+                // 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;
+                final 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;
                 }
-                else 
+                try
                 {
-                    ValueExpression expression = _currentUISelectItems.getValueExpression("value");
-                    throw new IllegalArgumentException(
-                        _collectionLabel + " referenced by UISelectItems with binding '"
-                        + expression.getExpressionString()
-                        + "' and Component-Path : " + RendererUtils.getPathToComponent(_currentUISelectItems)
-                        + " does not contain Objects of type SelectItem"
-                        + " or does not provide the attribute itemValue");
+                    Object itemValue = attributeMap.get(ITEM_VALUE_ATTR);
+                    if(itemValue != 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)); 
+                    }
+                    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");
+                    }
+                }
+                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 item;
+            return (SelectItem) item;
         }
         throw new NoSuchElementException();
     }
@@ -248,4 +307,25 @@
     {
         throw new UnsupportedOperationException();
     }
+    
+    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());
+        }
+    }
+
 }