Coverage Report - org.apache.myfaces.shared.renderkit._SharedRendererUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
_SharedRendererUtils
4%
6/132
2%
2/82
10.143
 
 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 org.apache.myfaces.shared.renderkit;
 20  
 
 21  
 import java.lang.reflect.Array;
 22  
 import java.lang.reflect.Method;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Arrays;
 25  
 import java.util.Collection;
 26  
 import java.util.HashSet;
 27  
 import java.util.Iterator;
 28  
 import java.util.LinkedList;
 29  
 import java.util.Queue;
 30  
 import java.util.Set;
 31  
 import java.util.SortedSet;
 32  
 import java.util.TreeSet;
 33  
 import java.util.logging.Level;
 34  
 import java.util.logging.Logger;
 35  
 
 36  
 import javax.el.ValueExpression;
 37  
 import javax.faces.FacesException;
 38  
 import javax.faces.component.UIOutput;
 39  
 import javax.faces.component.UISelectMany;
 40  
 import javax.faces.context.FacesContext;
 41  
 import javax.faces.convert.Converter;
 42  
 import javax.faces.convert.ConverterException;
 43  
 import javax.faces.model.SelectItem;
 44  
 import javax.faces.model.SelectItemGroup;
 45  
 
 46  
 import org.apache.myfaces.shared.util.SelectItemsIterator;
 47  
 
 48  
 /**
 49  
  * The util methods in this class are shared between the javax.faces.component package and
 50  
  * the org.apache.myfaces.renderkit package.
 51  
  * Please note: Any changes here must also apply to the class in the other package!
 52  
  */
 53  0
 class _SharedRendererUtils
 54  
 {
 55  
     static final String COLLECTION_TYPE_KEY = "collectionType";
 56  
     static final String VALUE_TYPE_KEY = "valueType";
 57  
 
 58  
     static Converter findUIOutputConverter(FacesContext facesContext,
 59  
             UIOutput component)
 60  
     {
 61  
         // Attention!
 62  
         // This code is duplicated in jsfapi component package.
 63  
         // If you change something here please do the same in the other class!
 64  
 
 65  1
         Converter converter = component.getConverter();
 66  1
         if (converter != null)
 67  
         {
 68  0
             return converter;
 69  
         }
 70  
 
 71  
         //Try to find out by value expression
 72  1
         ValueExpression expression = component.getValueExpression("value");
 73  1
         if (expression == null)
 74  
         {
 75  1
             return null;
 76  
         }
 77  
 
 78  0
         Class<?> valueType = expression.getType(facesContext.getELContext());
 79  0
         if (valueType == null)
 80  
         {
 81  0
             return null;
 82  
         }
 83  
 
 84  0
         if (Object.class.equals(valueType))
 85  
         {
 86  0
             return null; //There is no converter for Object class
 87  
         }
 88  
 
 89  
         try
 90  
         {
 91  0
             return facesContext.getApplication().createConverter(valueType);
 92  
         }
 93  0
         catch (FacesException e)
 94  
         {
 95  0
             log(facesContext, "No Converter for type " + valueType.getName()
 96  
                     + " found", e);
 97  0
             return null;
 98  
         }
 99  
     }
 100  
 
 101  
     static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,
 102  
             String[] submittedValue) throws ConverterException
 103  
     {
 104  0
         return  getConvertedUISelectManyValue(facesContext, component,
 105  
             submittedValue, false);
 106  
     }
 107  
 
 108  
     /**
 109  
      * Gets the converted value of a UISelectMany component.
 110  
      * If the considerValueType is true, this method will also consider the
 111  
      * valueType attribute of Tomahawk UISelectMany components.
 112  
      * 
 113  
      * @param facesContext
 114  
      * @param component
 115  
      * @param submittedValue
 116  
      * @param considerValueType
 117  
      * @return
 118  
      * @throws ConverterException
 119  
      */
 120  
     static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,  
 121  
             String[] submittedValue, boolean considerValueType) throws ConverterException
 122  
     {
 123  
         // Attention!
 124  
         // This code is duplicated in jsfapi component package (except for considerValueType).
 125  
         // If you change something here please do the same in the other class!
 126  
 
 127  0
         if (submittedValue == null)
 128  
         {
 129  0
             throw new NullPointerException("submittedValue");
 130  
         }
 131  
 
 132  0
         ValueExpression expression = component.getValueExpression("value");
 133  0
         Object targetForConvertedValues = null;
 134  
         
 135  
         // if the component has an attached converter, use it
 136  0
         Converter converter = component.getConverter();
 137  0
         if (converter == null && considerValueType)
 138  
         {
 139  
             // try to get a converter from the valueType attribute
 140  0
             converter = getValueTypeConverter(facesContext, component);
 141  
         }
 142  
         
 143  0
         if (expression != null)
 144  
         {
 145  0
             Class<?> modelType = expression
 146  
                     .getType(facesContext.getELContext());
 147  0
             if (modelType == null)
 148  
             {
 149  
                 // FIXME temporal workaround for MYFACES-2552
 150  0
                 return submittedValue;
 151  
             }
 152  0
             else if (modelType.isArray())
 153  
             {
 154  
                 // the target should be an array
 155  0
                 Class<?> componentType = modelType.getComponentType();
 156  
                 // check for optimization if the target is
 157  
                 // a string array --> no conversion needed
 158  0
                 if (String.class.equals(componentType))
 159  
                 {
 160  0
                     return submittedValue;
 161  
                 }
 162  0
                 if (converter == null)
 163  
                 {
 164  
                     // the compononent does not have an attached converter
 165  
                     // --> try to get a registered-by-class converter
 166  0
                     converter = facesContext.getApplication().createConverter(
 167  
                             componentType);
 168  
 
 169  0
                     if (converter == null)
 170  
                     {
 171  
                         // could not obtain a Converter
 172  
                         // --> check if we maybe do not really have to convert
 173  0
                         if (!Object.class.equals(componentType))
 174  
                         {
 175  
                             // target is not an Object array
 176  
                             // and not a String array (checked some lines above)
 177  
                             // and we do not have a Converter
 178  0
                             throw new ConverterException(
 179  
                                     "Could not obtain a Converter for "
 180  
                                             + componentType.getName());
 181  
                         }
 182  
                     }
 183  
                 }
 184  
                 // instantiate the array
 185  0
                 targetForConvertedValues = Array.newInstance(componentType,
 186  
                         submittedValue.length);
 187  0
             }
 188  0
             else if (Collection.class.isAssignableFrom(modelType) || Object.class.equals(modelType))
 189  
             {
 190  0
                 if (converter == null)
 191  
                 {
 192  
                     // try to get the by-type-converter from the type of the SelectItems
 193  0
                     SelectItemsIterator iterator = new SelectItemsIterator(component, facesContext);
 194  0
                     converter = getSelectItemsValueConverter(iterator, facesContext);
 195  
                 }
 196  
 
 197  0
                 Object collectionTypeAttr = component.getAttributes().get(
 198  
                         COLLECTION_TYPE_KEY);
 199  0
                 if (collectionTypeAttr != null)
 200  
                 {
 201  0
                     Class<?> collectionType = getClassFromAttribute(facesContext, collectionTypeAttr);
 202  0
                     if (collectionType == null)
 203  
                     {
 204  0
                         throw new FacesException(
 205  
                                 "The attribute "
 206  
                                         + COLLECTION_TYPE_KEY
 207  
                                         + " of component "
 208  
                                         + component.getClientId(facesContext)
 209  
                                         + " does not evaluate to a "
 210  
                                         + "String, a Class object or a ValueExpression pointing "
 211  
                                         + "to a String or a Class object.");
 212  
                     }
 213  
                     // now we have a collectionType --> but is it really some kind of Collection
 214  0
                     if (!Collection.class.isAssignableFrom(collectionType))
 215  
                     {
 216  0
                         throw new FacesException("The attribute "
 217  
                                 + COLLECTION_TYPE_KEY + " of component "
 218  
                                 + component.getClientId(facesContext)
 219  
                                 + " does not point to a valid type of Collection.");
 220  
                     }
 221  
                     // now we have a real collectionType --> try to instantiate it
 222  
                     try
 223  
                     {
 224  0
                         targetForConvertedValues = collectionType.newInstance();
 225  
                     }
 226  0
                     catch (Exception e)
 227  
                     {
 228  0
                         throw new FacesException("The Collection "
 229  
                                 + collectionType.getName()
 230  
                                 + "can not be instantiated.", e);
 231  0
                     }
 232  0
                 }
 233  0
                 else if (Collection.class.isAssignableFrom(modelType))
 234  
                 {
 235  
                     // component.getValue() will implement Collection at this point
 236  0
                     Collection<?> componentValue = (Collection<?>) component
 237  
                             .getValue();
 238  
                     // can we clone the Collection
 239  0
                     if (componentValue instanceof Cloneable)
 240  
                     {
 241  
                         // clone method of Object is protected --> use reflection
 242  
                         try
 243  
                         {
 244  0
                             Method cloneMethod = componentValue.getClass()
 245  
                                     .getMethod("clone");
 246  0
                             Collection<?> clone = (Collection<?>) cloneMethod
 247  
                                     .invoke(componentValue);
 248  0
                             clone.clear();
 249  0
                             targetForConvertedValues = clone;
 250  
                         }
 251  0
                         catch (Exception e)
 252  
                         {
 253  0
                             log(facesContext, "Could not clone "
 254  
                                     + componentValue.getClass().getName(), e);
 255  0
                         }
 256  
                     }
 257  
 
 258  
                     // if clone did not work
 259  0
                     if (targetForConvertedValues == null)
 260  
                     {
 261  
                         // try to create the (concrete) collection from modelType 
 262  
                         // or with the class object of componentValue (if any)
 263  
                         try
 264  
                         {
 265  0
                             targetForConvertedValues = (componentValue != null 
 266  
                                     ? componentValue.getClass()
 267  
                                     : modelType).newInstance();
 268  
                         }
 269  0
                         catch (Exception e)
 270  
                         {
 271  
                             // this did not work either
 272  
                             // use the standard concrete type
 273  0
                             if (SortedSet.class.isAssignableFrom(modelType))
 274  
                             {
 275  0
                                 targetForConvertedValues = new TreeSet();
 276  
                             }
 277  0
                             else if (Queue.class.isAssignableFrom(modelType))
 278  
                             {
 279  0
                                 targetForConvertedValues = new LinkedList();
 280  
                             }
 281  0
                             else if (Set.class.isAssignableFrom(modelType))
 282  
                             {
 283  0
                                 targetForConvertedValues = new HashSet(
 284  
                                         submittedValue.length);
 285  
                             }
 286  
                             else
 287  
                             {
 288  0
                                 targetForConvertedValues = new ArrayList(
 289  
                                         submittedValue.length);
 290  
                             }
 291  0
                         }
 292  
                     }
 293  0
                 }
 294  
                 else /* if (Object.class.equals(modelType)) */
 295  
                 {
 296  
                     // a modelType of Object is also permitted, in order to support
 297  
                     // managed bean properties of type Object
 298  
                     
 299  
                     // optimization: if we don't have a converter, we can return the submittedValue
 300  0
                     if (converter == null)
 301  
                     {
 302  0
                         return submittedValue;
 303  
                     }
 304  
                     
 305  0
                     targetForConvertedValues = new Object[submittedValue.length];
 306  
                 }
 307  0
             }
 308  
             else
 309  
             {
 310  
                 // the expression does neither point to an array nor to a collection
 311  0
                 throw new ConverterException(
 312  
                         "ValueExpression for UISelectMany must be of type Collection or Array.");
 313  
             }
 314  0
         }
 315  
         else
 316  
         {
 317  0
             targetForConvertedValues = new Object[submittedValue.length];
 318  
         }
 319  
 
 320  
         // convert the values with the selected converter (if any)
 321  
         // and store them in targetForConvertedValues
 322  0
         boolean isArray = (targetForConvertedValues.getClass().isArray());
 323  0
         for (int i = 0; i < submittedValue.length; i++)
 324  
         {
 325  
             // get the value
 326  
             Object value;
 327  0
             if (converter != null)
 328  
             {
 329  0
                 value = converter.getAsObject(facesContext, component,
 330  
                         submittedValue[i]);
 331  
             }
 332  
             else
 333  
             {
 334  0
                 value = submittedValue[i];
 335  
             }
 336  
             // store it in targetForConvertedValues
 337  0
             if (isArray)
 338  
             {
 339  0
                 Array.set(targetForConvertedValues, i, value);
 340  
             }
 341  
             else
 342  
             {
 343  0
                 ((Collection) targetForConvertedValues).add(value);
 344  
             }
 345  
         }
 346  
 
 347  0
         return targetForConvertedValues;
 348  
     }
 349  
     
 350  
     /**
 351  
      * Gets a Class object from a given component attribute. The attribute can
 352  
      * be a ValueExpression (that evaluates to a String or a Class) or a 
 353  
      * String (that is a fully qualified Java class name) or a Class object.
 354  
      * 
 355  
      * @param facesContext
 356  
      * @param attribute
 357  
      * @return
 358  
      * @throws FacesException if the value is a String and the represented
 359  
      *                        class cannot be found
 360  
      */
 361  
     static Class<?> getClassFromAttribute(FacesContext facesContext,
 362  
             Object attribute) throws FacesException
 363  
     {
 364  
         // Attention!
 365  
         // This code is duplicated in jsfapi component package.
 366  
         // If you change something here please do the same in the other class!
 367  
         
 368  0
         Class<?> type = null;
 369  
         
 370  
         // if there is a value, it must be a ...
 371  
         // ... a ValueExpression that evaluates to a String or a Class
 372  0
         if (attribute instanceof ValueExpression)
 373  
         {
 374  
             // get the value of the ValueExpression
 375  0
             attribute = ((ValueExpression) attribute)
 376  
                     .getValue(facesContext.getELContext());
 377  
         }
 378  
         // ... String that is a fully qualified Java class name
 379  0
         if (attribute instanceof String)
 380  
         {
 381  
             try
 382  
             {
 383  0
                 type = Class.forName((String) attribute);
 384  
             }
 385  0
             catch (ClassNotFoundException cnfe)
 386  
             {
 387  0
                 throw new FacesException(
 388  
                         "Unable to find class "
 389  
                                 + attribute
 390  
                                 + " on the classpath.", cnfe);
 391  0
             }
 392  
 
 393  
         }
 394  
         // ... a Class object
 395  0
         else if (attribute instanceof Class)
 396  
         {
 397  0
             type = (Class<?>) attribute;
 398  
         }
 399  
         
 400  0
         return type;
 401  
     }
 402  
     
 403  
     /**
 404  
      * Uses the valueType attribute of the given UISelectMany component to
 405  
      * get a by-type converter.
 406  
      * 
 407  
      * @param facesContext
 408  
      * @param component
 409  
      * @return
 410  
      */
 411  
     static Converter getValueTypeConverter(FacesContext facesContext, UISelectMany component)
 412  
     {
 413  0
         Converter converter = null;
 414  
         
 415  0
         Object valueTypeAttr = component.getAttributes().get(VALUE_TYPE_KEY);
 416  0
         if (valueTypeAttr != null)
 417  
         {
 418  
             // treat the valueType attribute exactly like the collectionType attribute
 419  0
             Class<?> valueType = getClassFromAttribute(facesContext, valueTypeAttr);
 420  0
             if (valueType == null)
 421  
             {
 422  0
                 throw new FacesException(
 423  
                         "The attribute "
 424  
                                 + VALUE_TYPE_KEY
 425  
                                 + " of component "
 426  
                                 + component.getClientId(facesContext)
 427  
                                 + " does not evaluate to a "
 428  
                                 + "String, a Class object or a ValueExpression pointing "
 429  
                                 + "to a String or a Class object.");
 430  
             }
 431  
             // now we have a valid valueType
 432  
             // --> try to get a registered-by-class converter
 433  0
             converter = facesContext.getApplication().createConverter(valueType);
 434  
             
 435  0
             if (converter == null)
 436  
             {
 437  0
                 log.log(Level.WARNING, "Found attribute valueType on component " +
 438  
                         RendererUtils.getPathToComponent(component) +
 439  
                         ", but could not get a by-type converter for type " + 
 440  
                         valueType.getName());
 441  
             }
 442  
         }
 443  
         
 444  0
         return converter;
 445  
     }
 446  
     
 447  
     /**
 448  
      * Iterates through the SelectItems with the given Iterator and tries to obtain
 449  
      * a by-class-converter based on the Class of SelectItem.getValue().
 450  
      * @param iterator
 451  
      * @param facesContext
 452  
      * @return The first suitable Converter for the given SelectItems or null.
 453  
      */
 454  
     static Converter getSelectItemsValueConverter(Iterator<SelectItem> iterator, FacesContext facesContext)
 455  
     {
 456  
         // Attention!
 457  
         // This code is duplicated in jsfapi component package.
 458  
         // If you change something here please do the same in the other class!
 459  
         
 460  0
         Converter converter = null;
 461  0
         while (converter == null && iterator.hasNext())
 462  
         {
 463  0
             SelectItem item = iterator.next();
 464  0
             if (item instanceof SelectItemGroup)
 465  
             {
 466  0
                 Iterator<SelectItem> groupIterator = Arrays.asList(
 467  
                         ((SelectItemGroup) item).getSelectItems()).iterator();
 468  0
                 converter = getSelectItemsValueConverter(groupIterator, facesContext);
 469  0
             }
 470  
             else
 471  
             {
 472  0
                 Class<?> selectItemsType = item.getValue().getClass();
 473  
                 
 474  
                 // optimization: no conversion for String values
 475  0
                 if (String.class.equals(selectItemsType))
 476  
                 {
 477  0
                     return null;
 478  
                 }
 479  
                 
 480  
                 try
 481  
                 {
 482  0
                     converter = facesContext.getApplication().createConverter(selectItemsType);
 483  
                 }
 484  0
                 catch (FacesException e)
 485  
                 {
 486  
                     // nothing - try again
 487  0
                 }
 488  
             }
 489  0
         }
 490  0
         return converter;
 491  
     }
 492  
 
 493  
     //private static final Log log = LogFactory.getLog(_SharedRendererUtils.class);
 494  1
     private static final Logger log = Logger.getLogger(_SharedRendererUtils.class.getName());
 495  
 
 496  
     /**
 497  
      * This method is different in the two versions of _SharedRendererUtils.
 498  
      */
 499  
     private static void log(FacesContext context, String msg, Exception e)
 500  
     {
 501  0
         log.log(Level.SEVERE, msg, e);
 502  0
     }
 503  
 }