Coverage Report - javax.faces.component._SelectItemsIterator
 
Classes in this File Line Coverage Branch Coverage Complexity
_SelectItemsIterator
59%
84/142
45%
39/86
8.625
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package javax.faces.component;
 20  
 
 21  
 import java.lang.reflect.Array;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Collection;
 24  
 import java.util.Iterator;
 25  
 import java.util.Map;
 26  
 import java.util.NoSuchElementException;
 27  
 import java.util.logging.Level;
 28  
 import java.util.logging.Logger;
 29  
 
 30  
 import javax.el.ValueExpression;
 31  
 import javax.faces.application.ProjectStage;
 32  
 import javax.faces.context.FacesContext;
 33  
 import javax.faces.model.SelectItem;
 34  
 
 35  
 // ATTENTION
 36  
 // This class is associated with org.apache.myfaces.shared.util.SelectItemsIterator.
 37  
 // Changes here should also be applied to this class.
 38  
 
 39  72
 class _SelectItemsIterator implements Iterator<SelectItem>
 40  
 {
 41  
     
 42  2
     private static final Logger log = Logger.getLogger(_SelectItemsIterator.class.getName());
 43  
     
 44  2
     private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR = new _EmptyIterator<UIComponent>();
 45  
     
 46  
     // org.apache.myfaces.shared.util.SelectItemsIterator uses JSFAttr
 47  
     private static final String VAR_ATTR = "var";
 48  
     private static final String ITEM_VALUE_ATTR = "itemValue";
 49  
     private static final String ITEM_LABEL_ATTR = "itemLabel";
 50  
     private static final String ITEM_DESCRIPTION_ATTR = "itemDescription";
 51  
     private static final String ITEM_DISABLED_ATTR = "itemDisabled";
 52  
     private static final String ITEM_LABEL_ESCAPED_ATTR = "itemLabelEscaped";
 53  
     private static final String NO_SELECTION_VALUE_ATTR = "noSelectionValue";
 54  
     
 55  
     private final Iterator<UIComponent> _children;
 56  
     private Iterator<?> _nestedItems;
 57  
     private SelectItem _nextItem;
 58  
     private UIComponent _currentComponent;
 59  
     private UISelectItems _currentUISelectItems;
 60  
     private FacesContext _facesContext;
 61  
 
 62  
     public _SelectItemsIterator(UIComponent selectItemsParent, FacesContext facesContext)
 63  30
     {
 64  30
         _children = selectItemsParent.getChildCount() > 0
 65  
                         ? selectItemsParent.getChildren().iterator()
 66  
                         : _EMPTY_UICOMPONENT_ITERATOR;
 67  30
         _facesContext = facesContext;
 68  30
     }
 69  
 
 70  
     @SuppressWarnings("unchecked")
 71  
     public boolean hasNext()
 72  
     {
 73  188
         if (_nextItem != null)
 74  
         {
 75  72
             return true;
 76  
         }
 77  116
         if (_nestedItems != null)
 78  
         {
 79  20
             if (_nestedItems.hasNext())
 80  
             {
 81  18
                 return true;
 82  
             }
 83  2
             _nestedItems = null;
 84  2
             _currentComponent = null;
 85  
         }
 86  98
         if (_children.hasNext())
 87  
         {
 88  76
             UIComponent child = _children.next();
 89  
             // When there is other components nested that does
 90  
             // not extends from UISelectItem or UISelectItems
 91  
             // the behavior for this iterator is just skip this
 92  
             // element(s) until an element that extends from these
 93  
             // classes are found. If there is no more elements
 94  
             // that conform this condition, just return false.
 95  76
             while (!(child instanceof UISelectItem) && !(child instanceof UISelectItems))
 96  
             {
 97  
                 // Try to skip it
 98  0
                 if (_children.hasNext())
 99  
                 {
 100  
                     // Skip and do the same check
 101  0
                     child = _children.next();
 102  
                 }
 103  
                 else
 104  
                 {
 105  
                     // End loop, so the final result is return false,
 106  
                     // since there are no more components to iterate.
 107  0
                     return false;
 108  
                 }
 109  
             }
 110  76
             if (child instanceof UISelectItem)
 111  
             {
 112  72
                 UISelectItem uiSelectItem = (UISelectItem) child;
 113  72
                 Object item = uiSelectItem.getValue();
 114  72
                 if (item == null)
 115  
                 {
 116  
                     // no value attribute --> create the SelectItem out of the other attributes
 117  72
                     Object itemValue = uiSelectItem.getItemValue();
 118  72
                     String label = uiSelectItem.getItemLabel();
 119  72
                     String description = uiSelectItem.getItemDescription();
 120  72
                     boolean disabled = uiSelectItem.isItemDisabled();
 121  72
                     boolean escape = uiSelectItem.isItemEscaped();
 122  72
                     boolean noSelectionOption = uiSelectItem.isNoSelectionOption();
 123  72
                     if (label == null && itemValue != null)
 124  
                     {
 125  72
                         label = itemValue.toString();
 126  
                     }
 127  72
                     item = new SelectItem(itemValue, label, description, disabled, escape, noSelectionOption);
 128  72
                 }
 129  0
                 else if (!(item instanceof SelectItem))
 130  
                 {
 131  0
                     ValueExpression expression = uiSelectItem.getValueExpression("value");
 132  0
                     throw new IllegalArgumentException("ValueExpression '"
 133  
                             + (expression == null ? null : expression.getExpressionString()) + "' of UISelectItem : "
 134  
                             + getPathToComponent(child) + " does not reference an Object of type SelectItem");
 135  
                 }
 136  72
                 _nextItem = (SelectItem) item;
 137  72
                 _currentComponent = child;
 138  72
                 return true;
 139  
             }
 140  4
             else if (child instanceof UISelectItems)
 141  
             {
 142  4
                 _currentUISelectItems = ((UISelectItems) child);
 143  4
                 Object value = _currentUISelectItems.getValue();
 144  4
                 _currentComponent = child;
 145  
 
 146  4
                 if (value instanceof SelectItem)
 147  
                 {
 148  0
                     _nextItem = (SelectItem) value;
 149  0
                     return true;
 150  
                 }
 151  4
                 else if (value != null && value.getClass().isArray())
 152  
                 {
 153  
                     // value is any kind of array (primitive or non-primitive)
 154  
                     // --> we have to use class Array to get the values
 155  2
                     int length = Array.getLength(value);
 156  2
                     Collection<Object> items = new ArrayList<Object>(length);
 157  8
                     for (int i = 0; i < length; i++)
 158  
                     {
 159  6
                         items.add(Array.get(value, i));
 160  
                     }
 161  2
                     _nestedItems = items.iterator();
 162  2
                     return hasNext();
 163  
                 }
 164  2
                 else if (value instanceof Iterable)
 165  
                 {
 166  
                     // value is Iterable --> Collection, DataModel,...
 167  2
                     _nestedItems = ((Iterable<?>) value).iterator();
 168  2
                     return hasNext();
 169  
                 }
 170  0
                 else if (value instanceof Map)
 171  
                 {
 172  0
                     Map<Object, Object> map = ((Map<Object, Object>) value);
 173  0
                     Collection<SelectItem> items = new ArrayList<SelectItem>(map.size());
 174  0
                     for (Map.Entry<Object, Object> entry : map.entrySet())
 175  
                     {
 176  0
                         items.add(new SelectItem(entry.getValue(), entry.getKey().toString()));
 177  0
                     }
 178  
                     
 179  0
                     _nestedItems = items.iterator();
 180  0
                     return hasNext();
 181  
                 }
 182  
                 else
 183  
                 {
 184  0
                     Level level = Level.FINE;
 185  0
                     if (!_facesContext.isProjectStage(ProjectStage.Production))
 186  
                     {
 187  0
                         level = Level.WARNING;
 188  
                     }
 189  
 
 190  0
                     if (log.isLoggable(level))
 191  
                     {
 192  0
                         ValueExpression expression = _currentUISelectItems.getValueExpression("value");
 193  0
                         log.log(level, "ValueExpression {0} of UISelectItems with component-path {1}"
 194  
                                 + " does not reference an Object of type SelectItem,"
 195  
                                 + " array, Iterable or Map, but of type: {2}",
 196  
                                 new Object[] {
 197  
                                     (expression == null ? null : expression.getExpressionString()),
 198  
                                     getPathToComponent(child),
 199  
                                     (value == null ? null : value.getClass().getName()) 
 200  
                                 });
 201  
                     }
 202  
                 }
 203  0
             }
 204  
             else
 205  
             {
 206  0
                 _currentComponent = null;
 207  
             }
 208  
         }
 209  22
         return false;
 210  
     }
 211  
 
 212  
     public SelectItem next()
 213  
     {
 214  84
         if (!hasNext())
 215  
         {
 216  0
             throw new NoSuchElementException();
 217  
         }
 218  84
         if (_nextItem != null)
 219  
         {
 220  72
             SelectItem value = _nextItem;
 221  72
             _nextItem = null;
 222  72
             return value;
 223  
         }
 224  12
         if (_nestedItems != null)
 225  
         {
 226  12
             Object item = _nestedItems.next();
 227  
             
 228  12
             if (!(item instanceof SelectItem))
 229  
             {
 230  
                 // check new params of SelectItems (since 2.0): itemValue, itemLabel, itemDescription,...
 231  
                 // Note that according to the spec UISelectItems does not provide Getter and Setter 
 232  
                 // methods for this values, so we have to use the attribute map
 233  12
                 Map<String, Object> attributeMap = _currentUISelectItems.getAttributes();
 234  
                 
 235  
                 // write the current item into the request map under the key listed in var, if available
 236  12
                 boolean wroteRequestMapVarValue = false;
 237  12
                 Object oldRequestMapVarValue = null;
 238  12
                 String var = (String) attributeMap.get(VAR_ATTR);
 239  12
                 if(var != null && !"".equals(var))
 240  
                 {
 241  
                     // save the current value of the key listed in var from the request map
 242  12
                     oldRequestMapVarValue = _facesContext.getExternalContext().getRequestMap().put(var, item);
 243  12
                     wroteRequestMapVarValue = true;
 244  
                 }
 245  
                 
 246  
                 // check the itemValue attribute
 247  12
                 Object itemValue = attributeMap.get(ITEM_VALUE_ATTR);
 248  12
                 if (itemValue == null)
 249  
                 {
 250  
                     // the itemValue attribute was not provided
 251  
                     // --> use the current item as the itemValue
 252  0
                     itemValue = item;
 253  
                 }
 254  
                 
 255  
                 // Spec: When iterating over the select items, toString() 
 256  
                 // must be called on the string rendered attribute values
 257  12
                 Object itemLabel = attributeMap.get(ITEM_LABEL_ATTR);
 258  12
                 if (itemLabel == null)
 259  
                 {
 260  12
                     if (itemValue != null)
 261  
                     {
 262  12
                         itemLabel = itemValue.toString();
 263  
                     }
 264  
                 }
 265  
                 else
 266  
                 {
 267  0
                     itemLabel = itemLabel.toString();
 268  
                 }
 269  12
                 Object itemDescription = attributeMap.get(ITEM_DESCRIPTION_ATTR);
 270  12
                 if (itemDescription != null)
 271  
                 {
 272  0
                     itemDescription = itemDescription.toString();
 273  
                 }
 274  12
                 Boolean itemDisabled = getBooleanAttribute(_currentUISelectItems, ITEM_DISABLED_ATTR, false);
 275  12
                 Boolean itemLabelEscaped = getBooleanAttribute(_currentUISelectItems, ITEM_LABEL_ESCAPED_ATTR, true);
 276  12
                 Object noSelectionValue = attributeMap.get(NO_SELECTION_VALUE_ATTR);
 277  12
                 item = new SelectItem(itemValue,
 278  
                         (String) itemLabel,
 279  
                         (String) itemDescription,
 280  
                         itemDisabled,
 281  
                         itemLabelEscaped,
 282  
                         itemValue.equals(noSelectionValue)); 
 283  
                     
 284  
                 // remove the value with the key from var from the request map, if previously written
 285  12
                 if(wroteRequestMapVarValue)
 286  
                 {
 287  
                     // If there was a previous value stored with the key from var in the request map, restore it
 288  12
                     if (oldRequestMapVarValue != null)
 289  
                     {
 290  0
                         _facesContext.getExternalContext()
 291  
                                 .getRequestMap().put(var, oldRequestMapVarValue);
 292  
                     }
 293  
                     else
 294  
                     {
 295  12
                         _facesContext.getExternalContext()
 296  
                                 .getRequestMap().remove(var);
 297  
                     }
 298  
                 } 
 299  
             }
 300  12
             return (SelectItem) item;
 301  
         }
 302  0
         throw new NoSuchElementException();
 303  
     }
 304  
 
 305  
     public void remove()
 306  
     {
 307  0
         throw new UnsupportedOperationException();
 308  
     }
 309  
     
 310  
     public UIComponent getCurrentComponent()
 311  
     {
 312  0
         return _currentComponent;
 313  
     }
 314  
 
 315  
     private boolean getBooleanAttribute(UIComponent component, String attrName, boolean defaultValue)
 316  
     {
 317  24
         Object value = component.getAttributes().get(attrName);
 318  24
         if (value == null)
 319  
         {
 320  24
             return defaultValue;
 321  
         }
 322  0
         else if (value instanceof Boolean)
 323  
         {
 324  0
             return (Boolean) value;
 325  
         }
 326  
         else
 327  
         {
 328  
             // If the value is a String, parse the boolean.
 329  
             // This makes the following code work: <tag attribute="true" />,
 330  
             // otherwise you would have to write <tag attribute="#{true}" />.
 331  0
             return Boolean.valueOf(value.toString());
 332  
         }
 333  
     }
 334  
 
 335  
     private String getPathToComponent(UIComponent component)
 336  
     {
 337  0
         StringBuffer buf = new StringBuffer();
 338  
 
 339  0
         if (component == null)
 340  
         {
 341  0
             buf.append("{Component-Path : ");
 342  0
             buf.append("[null]}");
 343  0
             return buf.toString();
 344  
         }
 345  
 
 346  0
         getPathToComponent(component, buf);
 347  
 
 348  0
         buf.insert(0, "{Component-Path : ");
 349  0
         buf.append("}");
 350  
 
 351  0
         return buf.toString();
 352  
     }
 353  
 
 354  
     private void getPathToComponent(UIComponent component, StringBuffer buf)
 355  
     {
 356  0
         if (component == null)
 357  
         {
 358  0
             return;
 359  
         }
 360  
 
 361  0
         StringBuffer intBuf = new StringBuffer();
 362  
 
 363  0
         intBuf.append("[Class: ");
 364  0
         intBuf.append(component.getClass().getName());
 365  0
         if (component instanceof UIViewRoot)
 366  
         {
 367  0
             intBuf.append(",ViewId: ");
 368  0
             intBuf.append(((UIViewRoot) component).getViewId());
 369  
         }
 370  
         else
 371  
         {
 372  0
             intBuf.append(",Id: ");
 373  0
             intBuf.append(component.getId());
 374  
         }
 375  0
         intBuf.append("]");
 376  
 
 377  0
         buf.insert(0, intBuf);
 378  
 
 379  0
         getPathToComponent(component.getParent(), buf);
 380  0
     }
 381  
 }