Coverage Report - org.apache.myfaces.el.unified.resolver.CompositeComponentELResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
CompositeComponentELResolver
0%
0/69
0%
0/54
3.435
CompositeComponentELResolver$1
N/A
N/A
3.435
CompositeComponentELResolver$CompositeComponentAttributesMapWrapper
0%
0/53
0%
0/34
3.435
 
 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.el.unified.resolver;
 20  
 
 21  
 import java.beans.BeanInfo;
 22  
 import java.beans.FeatureDescriptor;
 23  
 import java.beans.PropertyDescriptor;
 24  
 import java.lang.ref.WeakReference;
 25  
 import java.util.Collection;
 26  
 import java.util.Iterator;
 27  
 import java.util.Map;
 28  
 import java.util.Set;
 29  
 import java.util.WeakHashMap;
 30  
 
 31  
 import javax.el.ELContext;
 32  
 import javax.el.ELResolver;
 33  
 import javax.el.ValueExpression;
 34  
 import javax.faces.component.UIComponent;
 35  
 import javax.faces.context.FacesContext;
 36  
 import javax.faces.el.CompositeComponentExpressionHolder;
 37  
 
 38  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 39  
 import org.apache.myfaces.shared.util.ClassUtils;
 40  
 import org.apache.myfaces.view.facelets.tag.composite.CompositeComponentBeanInfo;
 41  
 
 42  
 /**
 43  
  * Composite component attribute EL resolver.  See JSF spec, section 5.6.2.2.
 44  
  */
 45  
 
 46  0
 public final class CompositeComponentELResolver extends ELResolver
 47  
 {
 48  
     private static final String ATTRIBUTES_MAP = "attrs";
 49  
     
 50  
     private static final String PARENT_COMPOSITE_COMPONENT = "parent";
 51  
     
 52  
     private static final String COMPOSITE_COMPONENT_ATTRIBUTES_MAPS = 
 53  
         "org.apache.myfaces.COMPOSITE_COMPONENT_ATTRIBUTES_MAPS";
 54  
 
 55  
     @Override
 56  
     public Class<?> getCommonPropertyType(ELContext context, Object base)
 57  
     {
 58  
         // Per the spec, return String.class.
 59  
 
 60  0
         return String.class;
 61  
     }
 62  
 
 63  
     @Override
 64  
     public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
 65  
             Object base)
 66  
     {
 67  
         // Per the spec, do nothing.
 68  
 
 69  0
         return null;
 70  
     }
 71  
 
 72  
     @Override
 73  
     public Class<?> getType(ELContext context, Object base, Object property)
 74  
     {
 75  0
         if (base != null && property != null &&
 76  
              base instanceof CompositeComponentAttributesMapWrapper &&
 77  
              property instanceof String)
 78  
         {
 79  0
             FacesContext facesContext = facesContext(context);
 80  0
             if (facesContext == null)
 81  
             {
 82  0
                 facesContext = FacesContext.getCurrentInstance();
 83  
             }
 84  0
             if (facesContext == null)
 85  
             {
 86  0
                 return null;
 87  
             }
 88  0
             if (!MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isStrictJsf2CCELResolver())
 89  
             {
 90  
                 // handle JSF 2.2 spec revisions:
 91  
                 // code resembles that found in Mojarra because it originates from
 92  
                 // the same contributor, whose ICLA is on file
 93  0
                 Class<?> exprType = null;
 94  0
                 Class<?> metaType = null;
 95  
 
 96  0
                 CompositeComponentAttributesMapWrapper evalMap = (CompositeComponentAttributesMapWrapper) base;
 97  0
                 ValueExpression ve = evalMap.getExpression((String) property);
 98  0
                 if (ve != null)
 99  
                 {
 100  0
                     exprType = ve.getType(context);
 101  
                 }
 102  
 
 103  0
                 if (!"".equals(property))
 104  
                 {
 105  0
                     if (evalMap._propertyDescriptors != null)
 106  
                     {
 107  0
                         for (PropertyDescriptor pd : evalMap._propertyDescriptors)
 108  
                         {
 109  0
                             if (property.equals(pd.getName()))
 110  
                             {
 111  0
                                 metaType = resolveType(context, pd);
 112  0
                                 break;
 113  
                             }
 114  
                         }
 115  
                     }
 116  
                 }
 117  0
                 if (metaType != null)
 118  
                 {
 119  
                     // override exprType only if metaType is narrower:
 120  0
                     if (exprType == null || exprType.isAssignableFrom(metaType))
 121  
                     {
 122  0
                         context.setPropertyResolved(true);
 123  0
                         return metaType;
 124  
                     }
 125  
                 }
 126  0
                 return exprType;
 127  
             }
 128  
         }
 129  
 
 130  
         // Per the spec, return null.
 131  0
         return null;
 132  
     }
 133  
 
 134  
     // adapted from CompositeMetadataTargetImpl#getPropertyType():
 135  
     private static Class<?> resolveType(ELContext context, PropertyDescriptor pd)
 136  
     {
 137  0
         if (pd != null)
 138  
         {
 139  0
             Object type = pd.getValue("type");
 140  0
             if (type != null)
 141  
             {
 142  0
                 type = ((ValueExpression)type).getValue(context);
 143  0
                 if (type instanceof String)
 144  
                 {
 145  
                     try
 146  
                     {
 147  0
                         type = ClassUtils.javaDefaultTypeToClass((String)type);
 148  
                     }
 149  0
                     catch (ClassNotFoundException e)
 150  
                     {
 151  0
                         type = null;
 152  0
                     }
 153  
                 }
 154  0
                 return (Class<?>) type;
 155  
             }
 156  0
             return pd.getPropertyType();
 157  
         }
 158  
 
 159  0
         return null;
 160  
     }
 161  
 
 162  
     @Override
 163  
     public Object getValue(ELContext context, Object base, Object property)
 164  
     {
 165  
         // Per the spec: base must not be null, an instance of UIComponent, and a composite
 166  
         // component.  Property must be a String.
 167  
 
 168  0
         if ((base != null) && (base instanceof UIComponent)
 169  
                 && UIComponent.isCompositeComponent((UIComponent) base)
 170  
                 && (property != null))
 171  
         {
 172  0
             String propName = property.toString();
 173  0
             UIComponent baseComponent = (UIComponent) base;
 174  
 
 175  0
             if (propName.equals(ATTRIBUTES_MAP))
 176  
             {
 177  
                 // Return a wrapped map that delegates all calls except get() and put().
 178  
 
 179  0
                 context.setPropertyResolved(true);
 180  
 
 181  0
                 return _getCompositeComponentAttributesMapWrapper(baseComponent, context);
 182  
             }
 183  
 
 184  0
             else if (propName.equals(PARENT_COMPOSITE_COMPONENT))
 185  
             {
 186  
                 // Return the parent.
 187  
 
 188  0
                 context.setPropertyResolved(true);
 189  
 
 190  0
                 return UIComponent.getCompositeComponentParent(baseComponent);
 191  
             }
 192  
         }
 193  
 
 194  
         // Otherwise, spec says to do nothing (return null).
 195  
 
 196  0
         return null;
 197  
     }
 198  
     
 199  
     @SuppressWarnings("unchecked")
 200  
     private Map<String, Object> _getCompositeComponentAttributesMapWrapper(
 201  
             UIComponent baseComponent, ELContext elContext)
 202  
     {
 203  0
         Map<Object, Object> contextMap = (Map<Object, Object>) facesContext(
 204  
                 elContext).getAttributes();
 205  
 
 206  
         // We use a WeakHashMap<UIComponent, WeakReference<Map<String, Object>>> to
 207  
         // hold attribute map wrappers by two reasons:
 208  
         //
 209  
         // 1. The wrapper is used multiple times for a very short amount of time (in fact on current request).
 210  
         // 2. The original attribute map has an inner reference to UIComponent, so we need to wrap it
 211  
         //    with WeakReference.
 212  
         //
 213  0
         Map<UIComponent, WeakReference<Map<String, Object>>> compositeComponentAttributesMaps = 
 214  
             (Map<UIComponent, WeakReference<Map<String, Object>>>) contextMap
 215  
                 .get(COMPOSITE_COMPONENT_ATTRIBUTES_MAPS);
 216  
 
 217  0
         Map<String, Object> attributesMap = null;
 218  
         WeakReference<Map<String, Object>> weakReference;
 219  0
         if (compositeComponentAttributesMaps != null)
 220  
         {
 221  0
             weakReference = compositeComponentAttributesMaps.get(baseComponent);
 222  0
             if (weakReference != null)
 223  
             {
 224  0
                 attributesMap = weakReference.get();                
 225  
             }
 226  0
             if (attributesMap == null)
 227  
             {
 228  
                 //create a wrapper map
 229  0
                 attributesMap = new CompositeComponentAttributesMapWrapper(
 230  
                         baseComponent);
 231  0
                 compositeComponentAttributesMaps.put(baseComponent,
 232  
                         new WeakReference<Map<String, Object>>(attributesMap));
 233  
             }
 234  
         }
 235  
         else
 236  
         {
 237  
             //Create both required maps
 238  0
             attributesMap = new CompositeComponentAttributesMapWrapper(
 239  
                     baseComponent);
 240  0
             compositeComponentAttributesMaps = new WeakHashMap<UIComponent, WeakReference<Map<String, Object>>>();
 241  0
             compositeComponentAttributesMaps.put(baseComponent,
 242  
                     new WeakReference<Map<String, Object>>(attributesMap));
 243  0
             contextMap.put(COMPOSITE_COMPONENT_ATTRIBUTES_MAPS,
 244  
                     compositeComponentAttributesMaps);
 245  
         }
 246  0
         return attributesMap;
 247  
     }
 248  
     
 249  
     // get the FacesContext from the ELContext
 250  
     private static FacesContext facesContext(final ELContext context)
 251  
     {
 252  0
         return (FacesContext)context.getContext(FacesContext.class);
 253  
     }
 254  
 
 255  
     @Override
 256  
     public boolean isReadOnly(ELContext context, Object base, Object property)
 257  
     {
 258  
         // Per the spec, return true.
 259  
 
 260  0
         return true;
 261  
     }
 262  
 
 263  
     @Override
 264  
     public void setValue(ELContext context, Object base, Object property,
 265  
             Object value)
 266  
     {
 267  
         // Per the spec, do nothing.
 268  0
     }
 269  
 
 270  
     // Wrapper map for composite component attributes.  Follows spec, section 5.6.2.2, table 5-11.
 271  0
     private final class CompositeComponentAttributesMapWrapper 
 272  
             implements CompositeComponentExpressionHolder, Map<String, Object>
 273  
     {
 274  
 
 275  
         private final UIComponent _component;
 276  
         private final BeanInfo _beanInfo;
 277  
         private final Map<String, Object> _originalMap;
 278  
         private final PropertyDescriptor [] _propertyDescriptors;
 279  
         private final CompositeComponentBeanInfo _ccBeanInfo;
 280  
 
 281  
         private CompositeComponentAttributesMapWrapper(UIComponent component)
 282  0
         {
 283  0
             this._component = component;
 284  0
             this._originalMap = component.getAttributes();
 285  0
             this._beanInfo = (BeanInfo) _originalMap.get(UIComponent.BEANINFO_KEY);
 286  0
             this._propertyDescriptors = _beanInfo.getPropertyDescriptors();
 287  0
             this._ccBeanInfo = (this._beanInfo instanceof CompositeComponentBeanInfo) ?
 288  
                 (CompositeComponentBeanInfo) this._beanInfo : null;
 289  0
         }
 290  
 
 291  
         public ValueExpression getExpression(String name)
 292  
         {
 293  0
             ValueExpression valueExpr = _component.getValueExpression(name);
 294  
 
 295  0
             return valueExpr;
 296  
         }
 297  
 
 298  
         public void clear()
 299  
         {
 300  0
             _originalMap.clear();
 301  0
         }
 302  
 
 303  
         public boolean containsKey(Object key)
 304  
         {
 305  0
             boolean value = _originalMap.containsKey(key);
 306  0
             if (value)
 307  
             {
 308  0
                 return value;
 309  
             }
 310  
             else
 311  
             {
 312  0
                 if (_ccBeanInfo == null)
 313  
                 {
 314  0
                     for (PropertyDescriptor attribute : _propertyDescriptors)
 315  
                     {
 316  0
                         if (attribute.getName().equals(key))
 317  
                         {
 318  0
                             return attribute.getValue("default") != null;
 319  
                         }
 320  
                     }
 321  
                 }
 322  
                 else
 323  
                 {
 324  0
                     PropertyDescriptor attribute = _ccBeanInfo.getPropertyDescriptorsMap().get(key);
 325  0
                     if (attribute != null)
 326  
                     {
 327  0
                         return attribute.getValue("default") != null;
 328  
                     }
 329  
                 }
 330  
             }
 331  0
             return false;
 332  
         }
 333  
 
 334  
         public boolean containsValue(Object value)
 335  
         {
 336  0
             return _originalMap.containsValue(value);
 337  
         }
 338  
 
 339  
         public Set<java.util.Map.Entry<String, Object>> entrySet()
 340  
         {
 341  0
             return _originalMap.entrySet();
 342  
         }
 343  
 
 344  
         public Object get(Object key)
 345  
         {
 346  0
             Object obj = _originalMap.get(key);
 347  0
             if (obj != null)
 348  
             {
 349  
                 // _originalMap is a _ComponentAttributesMap and thus any
 350  
                 // ValueExpressions will be evaluated by the call to
 351  
                 // _originalMap.get(). The only case in which we really will
 352  
                 // get a ValueExpression here is when a ValueExpression itself
 353  
                 // is stored as an attribute. But in this case we really want to 
 354  
                 // get the ValueExpression. So we don't have to evaluate possible
 355  
                 // ValueExpressions here, but can return obj directly.
 356  0
                 return obj;
 357  
             }
 358  
             else
 359  
             {
 360  0
                 if (_ccBeanInfo == null)
 361  
                 {
 362  0
                     for (PropertyDescriptor attribute : _propertyDescriptors)
 363  
                     {
 364  0
                         if (attribute.getName().equals(key))
 365  
                         {
 366  0
                             obj = attribute.getValue("default");
 367  0
                             break;
 368  
                         }
 369  
                     }
 370  
                 }
 371  
                 else
 372  
                 {
 373  0
                     PropertyDescriptor attribute = _ccBeanInfo.getPropertyDescriptorsMap().get(key);
 374  0
                     if (attribute != null)
 375  
                     {
 376  0
                         obj = attribute.getValue("default");
 377  
                     }
 378  
                 }
 379  
                 // We have to check for a ValueExpression and also evaluate it
 380  
                 // here, because in the PropertyDescriptor the default values are
 381  
                 // always stored as (Tag-)ValueExpressions.
 382  0
                 if (obj != null && obj instanceof ValueExpression)
 383  
                 {
 384  0
                     return ((ValueExpression) obj).getValue(FacesContext.getCurrentInstance().getELContext());
 385  
                 }
 386  
                 else
 387  
                 {
 388  0
                     return obj;                    
 389  
                 }
 390  
             }
 391  
         }
 392  
         
 393  
         public boolean isEmpty()
 394  
         {
 395  0
             return _originalMap.isEmpty();
 396  
         }
 397  
 
 398  
         public Set<String> keySet()
 399  
         {
 400  0
             return _originalMap.keySet();
 401  
         }
 402  
 
 403  
         public Object put(String key, Object value)
 404  
         {
 405  0
             ValueExpression valueExpression = _component.getValueExpression(key);
 406  
             
 407  
             // Per the spec, if the result is a ValueExpression, call setValue().
 408  0
             if (valueExpression != null)
 409  
             {
 410  0
                 valueExpression.setValue(FacesContext.getCurrentInstance().getELContext(), value);
 411  
 
 412  0
                 return null;
 413  
             }
 414  
 
 415  
             // Really this map is used to resolve ValueExpressions like 
 416  
             // #{cc.attrs.somekey}, so the value returned is not expected to be used, 
 417  
             // but is better to delegate to keep the semantic of this method.
 418  0
             return _originalMap.put(key, value);
 419  
         }
 420  
 
 421  
         public void putAll(Map<? extends String, ? extends Object> m)
 422  
         {
 423  0
             for (String key : m.keySet())
 424  
             {
 425  0
                 put(key, m.get(key));
 426  0
             }
 427  0
         }
 428  
 
 429  
         public Object remove(Object key)
 430  
         {
 431  0
             return _originalMap.remove(key);
 432  
         }
 433  
 
 434  
         public int size()
 435  
         {
 436  0
             return _originalMap.size();
 437  
         }
 438  
 
 439  
         public Collection<Object> values()
 440  
         {
 441  0
             return _originalMap.values();
 442  
         }
 443  
     }
 444  
 }