Coverage Report - javax.faces.component.UISelectMany
 
Classes in this File Line Coverage Branch Coverage Complexity
UISelectMany
25%
39/156
16%
24/144
7.062
 
 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.Arrays;
 24  
 import java.util.Collection;
 25  
 import java.util.Collections;
 26  
 import java.util.Iterator;
 27  
 
 28  
 import javax.el.ValueExpression;
 29  
 import javax.faces.application.FacesMessage;
 30  
 import javax.faces.context.ExternalContext;
 31  
 import javax.faces.context.FacesContext;
 32  
 import javax.faces.convert.Converter;
 33  
 import javax.faces.convert.ConverterException;
 34  
 import javax.faces.el.ValueBinding;
 35  
 import javax.faces.model.SelectItem;
 36  
 import javax.faces.render.Renderer;
 37  
 
 38  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
 39  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperties;
 40  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
 41  
 
 42  
 /**
 43  
  * Base class for the various component classes that allow a user to select zero or more options from a set.
 44  
  * <p>
 45  
  * This is not an abstract class; java code can create an instance of this, configure its rendererType appropriately,
 46  
  * and add it to a view. However there is no tag class for this component, ie it cannot be added directly to a page when
 47  
  * using JSP (and other presentation technologies are expected to behave similarly). Instead, there is a family of
 48  
  * subclasses that extend this base functionality, and they do have tag classes.
 49  
  * </p>
 50  
  * <p>
 51  
  * See the javadoc for this class in the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF
 52  
  * Specification</a> for further details.
 53  
  * </p>
 54  
  */
 55  
 @JSFComponent(defaultRendererType = "javax.faces.Listbox")
 56  
 @JSFJspProperties
 57  
 (properties={
 58  
         @JSFJspProperty(name="hideNoSelectionOption", returnType="boolean"),
 59  
         @JSFJspProperty(name="collectionType", returnType="java.lang.String")
 60  
 }
 61  
 )
 62  
 public class UISelectMany extends UIInput
 63  
 {
 64  
     public static final String COMPONENT_TYPE = "javax.faces.SelectMany";
 65  
     public static final String COMPONENT_FAMILY = "javax.faces.SelectMany";
 66  
 
 67  
     public static final String INVALID_MESSAGE_ID = "javax.faces.component.UISelectMany.INVALID";
 68  
 
 69  
     public UISelectMany()
 70  26
     {
 71  26
         setRendererType("javax.faces.Listbox");
 72  26
     }
 73  
 
 74  
     @Override
 75  
     public String getFamily()
 76  
     {
 77  0
         return COMPONENT_FAMILY;
 78  
     }
 79  
     
 80  
     public Object[] getSelectedValues()
 81  
     {
 82  0
         return (Object[]) getValue();
 83  
     }
 84  
 
 85  
     public void setSelectedValues(Object[] selectedValues)
 86  
     {
 87  0
         setValue(selectedValues);
 88  0
     }
 89  
 
 90  
     /**
 91  
      * @deprecated Use getValueExpression instead
 92  
      */
 93  
     @Override
 94  
     public ValueBinding getValueBinding(String name)
 95  
     {
 96  0
         if (name == null)
 97  
         {
 98  0
             throw new NullPointerException("name");
 99  
         }
 100  0
         if (name.equals("selectedValues"))
 101  
         {
 102  0
             return super.getValueBinding("value");
 103  
         }
 104  
         else
 105  
         {
 106  0
             return super.getValueBinding(name);
 107  
         }
 108  
     }
 109  
 
 110  
     /**
 111  
      * @deprecated Use setValueExpression instead
 112  
      */
 113  
     @Override
 114  
     public void setValueBinding(String name, ValueBinding binding)
 115  
     {
 116  0
         if (name == null)
 117  
         {
 118  0
             throw new NullPointerException("name");
 119  
         }
 120  0
         if (name.equals("selectedValues"))
 121  
         {
 122  0
             super.setValueBinding("value", binding);
 123  
         }
 124  
         else
 125  
         {
 126  0
             super.setValueBinding(name, binding);
 127  
         }
 128  0
     }
 129  
 
 130  
     @Override
 131  
     public ValueExpression getValueExpression(String name)
 132  
     {
 133  74
         if (name == null)
 134  
         {
 135  0
             throw new NullPointerException("name");
 136  
         }
 137  74
         if (name.equals("selectedValues"))
 138  
         {
 139  0
             return super.getValueExpression("value");
 140  
         }
 141  
         else
 142  
         {
 143  74
             return super.getValueExpression(name);
 144  
         }
 145  
     }
 146  
 
 147  
     @Override
 148  
     public void setValueExpression(String name, ValueExpression binding)
 149  
     {
 150  14
         if (name == null)
 151  
         {
 152  0
             throw new NullPointerException("name");
 153  
         }
 154  14
         if (name.equals("selectedValues"))
 155  
         {
 156  0
             super.setValueExpression("value", binding);
 157  
         }
 158  
         else
 159  
         {
 160  14
             super.setValueExpression(name, binding);
 161  
         }
 162  14
     }
 163  
 
 164  
     /**
 165  
      * @return true if Objects are different (!)
 166  
      */
 167  
     @Override
 168  
     protected boolean compareValues(Object previous, Object value)
 169  
     {
 170  0
         if (previous == null)
 171  
         {
 172  
             // one is null, the other not
 173  0
             return value != null;
 174  
         }
 175  0
         else if (value == null)
 176  
         {
 177  
             // one is null, the other not
 178  0
             return true;
 179  
         }
 180  
         else
 181  
         {
 182  0
             if (previous instanceof Object[] && value instanceof Object[])
 183  
             {
 184  0
                 return compareObjectArrays((Object[]) previous, (Object[]) value);
 185  
             }
 186  0
             else if (previous instanceof Collection && value instanceof Collection)
 187  
             {
 188  0
                 return compareCollections((Collection<?>) previous, (Collection<?>) value);
 189  
             }
 190  0
             else if (previous.getClass().isArray() && value.getClass().isArray())
 191  
             {
 192  0
                 return comparePrimitiveArrays(previous, value);
 193  
             }
 194  
             else
 195  
             {
 196  
                 // Objects have different classes
 197  0
                 return true;
 198  
             }
 199  
         }
 200  
     }
 201  
 
 202  
     private boolean compareObjectArrays(Object[] previous, Object[] value)
 203  
     {
 204  0
         int length = value.length;
 205  0
         if (previous.length != length)
 206  
         {
 207  
             // different length
 208  0
             return true;
 209  
         }
 210  
 
 211  0
         boolean[] scoreBoard = new boolean[length];
 212  0
         for (int i = 0; i < length; i++)
 213  
         {
 214  0
             Object p = previous[i];
 215  0
             boolean found = false;
 216  0
             for (int j = 0; j < length; j++)
 217  
             {
 218  0
                 if (scoreBoard[j] == false)
 219  
                 {
 220  0
                     Object v = value[j];
 221  0
                     if ((p == null && v == null) || (p != null && v != null && p.equals(v)))
 222  
                     {
 223  0
                         scoreBoard[j] = true;
 224  0
                         found = true;
 225  0
                         break;
 226  
                     }
 227  
                 }
 228  
             }
 229  0
             if (!found)
 230  
             {
 231  0
                 return true; // current element of previous array not found in new array
 232  
             }
 233  
         }
 234  
 
 235  0
         return false; // arrays are identical
 236  
     }
 237  
 
 238  
     private boolean compareCollections(Collection<?> previous, Collection<?> value)
 239  
     {
 240  0
         int length = value.size();
 241  0
         if (previous.size() != length)
 242  
         {
 243  
             // different length
 244  0
             return true;
 245  
         }
 246  
 
 247  0
         boolean[] scoreBoard = new boolean[length];
 248  0
         for (Iterator<?> itPrevious = previous.iterator(); itPrevious.hasNext();)
 249  
         {
 250  0
             Object p = itPrevious.next();
 251  0
             boolean found = false;
 252  0
             int j = 0;
 253  0
             for (Iterator<?> itValue = value.iterator(); itValue.hasNext(); j++)
 254  
             {
 255  0
                 Object v = itValue.next();
 256  0
                 if (scoreBoard[j] == false)
 257  
                 {
 258  0
                     if ((p == null && v == null) || (p != null && v != null && p.equals(v)))
 259  
                     {
 260  0
                         scoreBoard[j] = true;
 261  0
                         found = true;
 262  0
                         break;
 263  
                     }
 264  
                 }
 265  
             }
 266  0
             if (!found)
 267  
             {
 268  0
                 return true; // current element of previous Collection not found in new Collection
 269  
             }
 270  0
         }
 271  
 
 272  0
         return false; // Collections are identical
 273  
     }
 274  
 
 275  
     private boolean comparePrimitiveArrays(Object previous, Object value)
 276  
     {
 277  0
         int length = Array.getLength(value);
 278  0
         if (Array.getLength(previous) != length)
 279  
         {
 280  
             // different length
 281  0
             return true;
 282  
         }
 283  
 
 284  0
         boolean[] scoreBoard = new boolean[length];
 285  0
         for (int i = 0; i < length; i++)
 286  
         {
 287  0
             Object p = Array.get(previous, i);
 288  0
             boolean found = false;
 289  0
             for (int j = 0; j < length; j++)
 290  
             {
 291  0
                 if (scoreBoard[j] == false)
 292  
                 {
 293  0
                     Object v = Array.get(value, j);
 294  0
                     if ((p == null && v == null) || (p != null && v != null && p.equals(v)))
 295  
                     {
 296  0
                         scoreBoard[j] = true;
 297  0
                         found = true;
 298  0
                         break;
 299  
                     }
 300  
                 }
 301  
             }
 302  0
             if (!found)
 303  
             {
 304  0
                 return true; // current element of previous array not found in new array
 305  
             }
 306  
         }
 307  
 
 308  0
         return false; // arrays are identical
 309  
     }
 310  
 
 311  
     @Override
 312  
     protected void validateValue(FacesContext context, Object convertedValue)
 313  
     {
 314  10
         Iterator<?> itemValues = _createItemValuesIterator(convertedValue);
 315  
 
 316  
         // verify that iterator was successfully created for convertedValue type
 317  10
         if (itemValues == null)
 318  
         {
 319  0
             _MessageUtils.addErrorMessage(context, this, INVALID_MESSAGE_ID, new Object[] { _MessageUtils.getLabel(
 320  
                 context, this) });
 321  0
             setValid(false);
 322  0
             return;
 323  
         }
 324  
 
 325  10
         boolean hasValues = itemValues.hasNext();
 326  
 
 327  
         // if UISelectMany is required, then there must be some selected values
 328  10
         if (isRequired() && !hasValues)
 329  
         {
 330  4
             if (getRequiredMessage() != null)
 331  
             {
 332  0
                 String requiredMessage = getRequiredMessage();
 333  0
                 context.addMessage(this.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
 334  
                     requiredMessage, requiredMessage));
 335  0
             }
 336  
             else
 337  
             {
 338  4
                 _MessageUtils.addErrorMessage(context, this, REQUIRED_MESSAGE_ID,
 339  
                     new Object[] { _MessageUtils.getLabel(context, this) });
 340  
             }
 341  4
             setValid(false);
 342  4
             return;
 343  
         }
 344  
 
 345  
         // run the validators if there are item values to validate, or 
 346  
         // if we are required to validate empty fields
 347  6
         if (hasValues  || shouldValidateEmptyFields(context))
 348  
         {
 349  6
             _ComponentUtils.callValidators(context, this, convertedValue);
 350  
         }
 351  
 
 352  6
         if (isValid() && hasValues)
 353  
         {
 354  
             // all selected values must match to the values of the available options
 355  
 
 356  6
             Collection<SelectItem> items = new ArrayList<SelectItem>();
 357  6
             for (Iterator<SelectItem> iter = new _SelectItemsIterator(this, context); iter.hasNext();)
 358  
             {
 359  18
                 items.add(iter.next());
 360  
             }
 361  6
             Converter converter = getConverter();
 362  18
             while (itemValues.hasNext())
 363  
             {
 364  12
                 Object itemValue = itemValues.next();
 365  
 
 366  
                 // selected value must match to one of the available options
 367  
                 // and if required is true it must not match an option with noSelectionOption set to true (since 2.0)
 368  12
                 if (!_SelectItemsUtil.matchValue(context, this, itemValue, items.iterator(), converter)
 369  
                         || (
 370  
                             this.isRequired()
 371  
                             && _SelectItemsUtil.isNoSelectionOption(context, this, itemValue,
 372  
                                                                     items.iterator(), converter)
 373  
                         ))
 374  
                 {    
 375  0
                     _MessageUtils.addErrorMessage(context, this, INVALID_MESSAGE_ID,
 376  
                         new Object[] { _MessageUtils.getLabel(context, this) });
 377  0
                     setValid(false);
 378  0
                     return;
 379  
                 }
 380  12
             }
 381  
         }
 382  6
     }
 383  
 
 384  
     @Override
 385  
     protected Object getConvertedValue(FacesContext context, Object submittedValue) throws ConverterException
 386  
     {
 387  0
         Renderer renderer = getRenderer(context);
 388  0
         if (renderer != null)
 389  
         {
 390  0
             return renderer.getConvertedValue(context, this, submittedValue);
 391  
         }
 392  0
         else if (submittedValue == null)
 393  
         {
 394  0
             return null;
 395  
         }
 396  0
         else if (submittedValue instanceof String[])
 397  
         {
 398  0
             return _SharedRendererUtils.getConvertedUISelectManyValue(context, this, (String[]) submittedValue);
 399  
         }
 400  0
         return submittedValue;
 401  
     }
 402  
 
 403  
     private Iterator<?> _createItemValuesIterator(Object convertedValue)
 404  
     {
 405  10
         if (convertedValue == null)
 406  
         {
 407  2
             return Collections.emptyList().iterator();
 408  
         }
 409  
         else
 410  
         {
 411  8
             Class<?> valueClass = convertedValue.getClass();
 412  8
             if (valueClass.isArray())
 413  
             {
 414  4
                 return new _PrimitiveArrayIterator(convertedValue);
 415  
             }
 416  4
             else if (convertedValue instanceof Object[])
 417  
             {
 418  0
                 Object[] values = (Object[]) convertedValue;
 419  0
                 return Arrays.asList(values).iterator();
 420  
             }
 421  4
             else if (convertedValue instanceof Collection)
 422  
             {
 423  4
                 Collection<?> values = (Collection<?>) convertedValue;
 424  4
                 return values.iterator();
 425  
             }
 426  
             else
 427  
             {
 428  
                 // unsupported type for iteration
 429  0
                 return null;
 430  
             }
 431  
         }
 432  
     }
 433  
     
 434  
     // Copied from javax.faces.component.UIInput
 435  
     private boolean shouldValidateEmptyFields(FacesContext context)
 436  
     {
 437  0
         ExternalContext ec = context.getExternalContext();
 438  0
         Boolean validateEmptyFields = (Boolean) ec.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
 439  
 
 440  0
         if (validateEmptyFields == null)
 441  
         {
 442  0
              String param = ec.getInitParameter(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
 443  
 
 444  
              // null means the same as auto.
 445  0
              if (param == null)
 446  
              {
 447  0
                  param = "auto";
 448  
              }
 449  
              else
 450  
              {
 451  
                  // The environment variables are case insensitive.
 452  0
                  param = param.toLowerCase();
 453  
              }
 454  
 
 455  0
              if (param.equals("auto") && _ExternalSpecifications.isBeanValidationAvailable())
 456  
              {
 457  0
                  validateEmptyFields = true;
 458  
              }
 459  0
              else if (param.equals("true"))
 460  
              {
 461  0
                  validateEmptyFields = true;
 462  
              }
 463  
              else
 464  
              {
 465  0
                  validateEmptyFields = false;
 466  
              }
 467  
 
 468  
              // cache the parsed value
 469  0
              ec.getApplicationMap().put(VALIDATE_EMPTY_FIELDS_PARAM_NAME, validateEmptyFields);
 470  
         }
 471  
 
 472  0
         return validateEmptyFields;
 473  
     }
 474  
 }