Coverage Report - org.apache.myfaces.view.facelets.tag.MetaRulesetImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
MetaRulesetImpl
0%
0/111
0%
0/62
3.667
MetaRulesetImpl$1
N/A
N/A
3.667
MetaRulesetImpl$NullMetadata
0%
0/2
N/A
3.667
 
 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.view.facelets.tag;
 20  
 
 21  
 import org.apache.myfaces.shared.util.ClassUtils;
 22  
 import org.apache.myfaces.view.facelets.util.ParameterCheck;
 23  
 
 24  
 import javax.faces.view.facelets.FaceletContext;
 25  
 import javax.faces.view.facelets.MetaRule;
 26  
 import javax.faces.view.facelets.MetaRuleset;
 27  
 import javax.faces.view.facelets.Metadata;
 28  
 import javax.faces.view.facelets.MetadataTarget;
 29  
 import javax.faces.view.facelets.Tag;
 30  
 import javax.faces.view.facelets.TagAttribute;
 31  
 import javax.faces.view.facelets.TagException;
 32  
 import java.beans.IntrospectionException;
 33  
 import java.util.ArrayList;
 34  
 import java.util.HashMap;
 35  
 import java.util.List;
 36  
 import java.util.Map;
 37  
 import java.util.WeakHashMap;
 38  
 import java.util.logging.Level;
 39  
 import java.util.logging.Logger;
 40  
 import org.apache.myfaces.view.facelets.PassthroughRule;
 41  
 import org.apache.myfaces.view.facelets.tag.jsf.PassThroughLibrary;
 42  
 
 43  
 /**
 44  
  * 
 45  
  * @author Jacob Hookom
 46  
  * @version $Id$
 47  
  */
 48  0
 public final class MetaRulesetImpl extends MetaRuleset
 49  
 {
 50  0
     private final static Metadata NONE = new NullMetadata();
 51  
 
 52  
     //private final static Logger log = Logger.getLogger("facelets.tag.meta");
 53  0
     private final static Logger log = Logger.getLogger(MetaRulesetImpl.class.getName());
 54  
 
 55  
     /**
 56  
      * Cache the MetadataTarget instances per ClassLoader using the Class-Name of the type variable
 57  
      * of MetadataTarget.
 58  
      * NOTE that we do it this way, because the only other valid way in order to support a shared
 59  
      * classloader scenario would be to use a WeakHashMap<Class<?>, MetadataTarget>, but this
 60  
      * creates a cyclic reference between the key and the value of the WeakHashMap which will
 61  
      * most certainly cause a memory leak! Furthermore we can manually cleanup the Map when
 62  
      * the webapp is undeployed just by removing the Map for the current ClassLoader. 
 63  
      */
 64  0
     private volatile static WeakHashMap<ClassLoader, Map<String, MetadataTarget>> metadata
 65  
             = new WeakHashMap<ClassLoader, Map<String, MetadataTarget>>();
 66  
 
 67  
     /**
 68  
      * Removes the cached MetadataTarget instances in order to prevent a memory leak.
 69  
      */
 70  
     public static void clearMetadataTargetCache()
 71  
     {
 72  0
         metadata.remove(ClassUtils.getContextClassLoader());
 73  0
     }
 74  
 
 75  
     private static Map<String, MetadataTarget> getMetaData()
 76  
     {
 77  0
         ClassLoader cl = ClassUtils.getContextClassLoader();
 78  
         
 79  0
         Map<String, MetadataTarget> metadata = (Map<String, MetadataTarget>)
 80  
                 MetaRulesetImpl.metadata.get(cl);
 81  
 
 82  0
         if (metadata == null)
 83  
         {
 84  
             // Ensure thread-safe put over _metadata, and only create one map
 85  
             // per classloader to hold metadata.
 86  0
             synchronized (MetaRulesetImpl.metadata)
 87  
             {
 88  0
                 metadata = createMetaData(cl, metadata);
 89  0
             }
 90  
         }
 91  
 
 92  0
         return metadata;
 93  
     }
 94  
     
 95  
     private static Map<String, MetadataTarget> createMetaData(ClassLoader cl, Map<String, MetadataTarget> metadata)
 96  
     {
 97  0
         metadata = (Map<String, MetadataTarget>) MetaRulesetImpl.metadata.get(cl);
 98  0
         if (metadata == null)
 99  
         {
 100  0
             metadata = new HashMap<String, MetadataTarget>();
 101  0
             MetaRulesetImpl.metadata.put(cl, metadata);
 102  
         }
 103  0
         return metadata;
 104  
     }
 105  
 
 106  0
     private final static TagAttribute[] EMPTY = new TagAttribute[0];
 107  
     
 108  
     private final Map<String, TagAttribute> _attributes;
 109  
     
 110  
     private final TagAttribute[] _passthroughAttributes;
 111  
 
 112  
     private final List<Metadata> _mappers;
 113  
 
 114  
     private final List<MetaRule> _rules;
 115  
 
 116  
     private final Tag _tag;
 117  
 
 118  
     private final Class<?> _type;
 119  
     
 120  
     private final List<MetaRule> _passthroughRules;
 121  
     
 122  
     public MetaRulesetImpl(Tag tag, Class<?> type)
 123  0
     {
 124  0
         _tag = tag;
 125  0
         _type = type;
 126  0
         TagAttribute[] allAttributes = _tag.getAttributes().getAll();
 127  
         // This map is proportional to the number of attributes defined, and usually
 128  
         // the properties with alias are very few, so set an initial size close to
 129  
         // the number of attributes is ok.
 130  0
         int initialSize = allAttributes.length > 0 ? (allAttributes.length * 4 + 3) / 3 : 4;
 131  0
         _attributes = new HashMap<String, TagAttribute>(initialSize);
 132  0
         _mappers = new ArrayList<Metadata>(initialSize);
 133  
         // Usually ComponentTagHandlerDelegate has 5 rules at max
 134  
         // and CompositeComponentResourceTagHandler 6, so 8 is a good number
 135  0
         _rules = new ArrayList<MetaRule>(8); 
 136  0
         _passthroughRules = new ArrayList<MetaRule>(2);
 137  
 
 138  
         // Passthrough attributes are different from normal attributes, because they
 139  
         // are just rendered into the markup without additional processing from the
 140  
         // renderer. Here it starts attribute processing, so this is the best place 
 141  
         // to find the passthrough attributes.
 142  0
         TagAttribute[] passthroughAttribute = _tag.getAttributes().getAll(
 143  
             PassThroughLibrary.NAMESPACE);
 144  0
         TagAttribute[] passthroughAttributeAlias = _tag.getAttributes().getAll(
 145  
             PassThroughLibrary.ALIAS_NAMESPACE);
 146  
         
 147  0
         if (passthroughAttribute.length > 0 ||
 148  
             passthroughAttributeAlias.length > 0)
 149  
         {
 150  0
             _passthroughAttributes = new TagAttribute[passthroughAttribute.length+
 151  
                 passthroughAttributeAlias.length];
 152  0
             int i = 0;
 153  0
             for (TagAttribute attribute : allAttributes)
 154  
             {
 155  
                 // The fastest check is check if the length is > 0, because
 156  
                 // most attributes usually has no namespace attached.
 157  0
                 if (attribute.getNamespace().length() > 0 &&
 158  
                     (PassThroughLibrary.NAMESPACE.equals(attribute.getNamespace()) ||
 159  
                         PassThroughLibrary.ALIAS_NAMESPACE.equals(attribute.getNamespace())))
 160  
                 {
 161  0
                     _passthroughAttributes[i] = attribute;
 162  0
                     i++;
 163  
                 }
 164  
                 else
 165  
                 {
 166  0
                     _attributes.put(attribute.getLocalName(), attribute);
 167  
                 }
 168  
             }
 169  0
         }
 170  
         else
 171  
         {
 172  0
             _passthroughAttributes = EMPTY;
 173  
             // setup attributes
 174  0
             for (TagAttribute attribute : allAttributes)
 175  
             {
 176  0
                 _attributes.put(attribute.getLocalName(), attribute);
 177  
             }
 178  
         }
 179  
 
 180  
         // add default rules
 181  0
         _rules.add(BeanPropertyTagRule.INSTANCE);
 182  0
     }
 183  
 
 184  
     public MetaRuleset add(Metadata mapper)
 185  
     {
 186  0
         ParameterCheck.notNull("mapper", mapper);
 187  
 
 188  0
         if (!_mappers.contains(mapper))
 189  
         {
 190  0
             _mappers.add(mapper);
 191  
         }
 192  
 
 193  0
         return this;
 194  
     }
 195  
 
 196  
     public MetaRuleset addRule(MetaRule rule)
 197  
     {
 198  0
         ParameterCheck.notNull("rule", rule);
 199  
 
 200  0
         if (rule instanceof PassthroughRule)
 201  
         {
 202  0
             _passthroughRules.add(rule);
 203  
         }
 204  
         else
 205  
         {
 206  0
             _rules.add(rule);
 207  
         }
 208  
 
 209  0
         return this;
 210  
     }
 211  
 
 212  
     public MetaRuleset alias(String attribute, String property)
 213  
     {
 214  0
         ParameterCheck.notNull("attribute", attribute);
 215  0
         ParameterCheck.notNull("property", property);
 216  
 
 217  0
         TagAttribute attr = (TagAttribute) _attributes.remove(attribute);
 218  0
         if (attr != null)
 219  
         {
 220  0
             _attributes.put(property, attr);
 221  
         }
 222  
 
 223  0
         return this;
 224  
     }
 225  
 
 226  
     public Metadata finish()
 227  
     {
 228  0
         MetadataTarget target = null;
 229  
         
 230  0
         assert !_rules.isEmpty();
 231  
         
 232  0
         if (!_attributes.isEmpty())
 233  
         {
 234  0
             target = this._getMetadataTarget();
 235  0
             int ruleEnd = _rules.size() - 1;
 236  
 
 237  
             // now iterate over attributes
 238  0
             for (Map.Entry<String, TagAttribute> entry : _attributes.entrySet())
 239  
             {
 240  0
                 Metadata data = null;
 241  
 
 242  0
                 int i = ruleEnd;
 243  
 
 244  
                 // First loop is always safe
 245  
                 do
 246  
                 {
 247  0
                     MetaRule rule = _rules.get(i);
 248  0
                     data = rule.applyRule(entry.getKey(), entry.getValue(), target);
 249  0
                     i--;
 250  0
                 } while (data == null && i >= 0);
 251  
 
 252  0
                 if (data == null)
 253  
                 {
 254  0
                     if (log.isLoggable(Level.SEVERE))
 255  
                     {
 256  0
                         log.severe(entry.getValue() + " Unhandled by MetaTagHandler for type " + _type.getName());
 257  
                     }
 258  
                 }
 259  
                 else
 260  
                 {
 261  0
                     _mappers.add(data);
 262  
                 }
 263  0
             }
 264  
         }
 265  
 
 266  0
         if (_passthroughAttributes.length > 0 &&
 267  
             _passthroughRules.size() > 0)
 268  
         {
 269  0
             if (target == null)
 270  
             {
 271  0
                 target = this._getMetadataTarget();
 272  
             }
 273  0
             int ruleEnd = _passthroughRules.size() - 1;
 274  
 
 275  
             // now iterate over attributes
 276  0
             for (TagAttribute passthroughAttribute : _passthroughAttributes)
 277  
             {
 278  0
                 Metadata data = null;
 279  
 
 280  0
                 int i = ruleEnd;
 281  
 
 282  
                 // First loop is always safe
 283  
                 do
 284  
                 {
 285  0
                     MetaRule rule = _passthroughRules.get(i);
 286  0
                     data = rule.applyRule(passthroughAttribute.getLocalName(),
 287  
                         passthroughAttribute, target);
 288  0
                     i--;
 289  0
                 } while (data == null && i >= 0);
 290  
 
 291  0
                 if (data == null)
 292  
                 {
 293  0
                     if (log.isLoggable(Level.SEVERE))
 294  
                     {
 295  0
                         log.severe(passthroughAttribute.getLocalName() + 
 296  
                             " Unhandled by MetaTagHandler for type " + _type.getName());
 297  
                     }
 298  
                 }
 299  
                 else
 300  
                 {
 301  0
                     _mappers.add(data);
 302  
                 }
 303  
             }
 304  
         }
 305  
 
 306  0
         if (_mappers.isEmpty())
 307  
         {
 308  0
             return NONE;
 309  
         }
 310  
         else
 311  
         {
 312  0
             return new MetadataImpl(_mappers.toArray(new Metadata[_mappers.size()]));
 313  
         }
 314  
     }
 315  
 
 316  
     public MetaRuleset ignore(String attribute)
 317  
     {
 318  0
         ParameterCheck.notNull("attribute", attribute);
 319  
 
 320  0
         _attributes.remove(attribute);
 321  
 
 322  0
         return this;
 323  
     }
 324  
 
 325  
     public MetaRuleset ignoreAll()
 326  
     {
 327  0
         _attributes.clear();
 328  
 
 329  0
         return this;
 330  
     }
 331  
 
 332  
     private MetadataTarget _getMetadataTarget()
 333  
     {
 334  0
         Map<String, MetadataTarget> metadata = getMetaData();
 335  0
         String metaKey = _type.getName();
 336  
 
 337  0
         MetadataTarget meta = metadata.get(metaKey);
 338  0
         if (meta == null)
 339  
         {
 340  
             try
 341  
             {
 342  0
                 meta = new MetadataTargetImpl(_type);
 343  
             }
 344  0
             catch (IntrospectionException e)
 345  
             {
 346  0
                 throw new TagException(_tag, "Error Creating TargetMetadata", e);
 347  0
             }
 348  
 
 349  0
             synchronized(metadata)
 350  
             {
 351  
                 // Use a synchronized block to ensure proper operation on concurrent use cases.
 352  
                 // This is a racy single check, because initialization over the same class could happen
 353  
                 // multiple times, but the same result is always calculated. The synchronized block 
 354  
                 // just ensure thread-safety, because only one thread will modify the cache map
 355  
                 // at the same time.
 356  0
                 metadata.put(metaKey, meta);
 357  0
             }
 358  
         }
 359  
 
 360  0
         return meta;
 361  
     }
 362  
 
 363  0
     private static class NullMetadata extends Metadata
 364  
     {
 365  
         /**
 366  
          * {@inheritDoc}
 367  
          */
 368  
         @Override
 369  
         public void applyMetadata(FaceletContext ctx, Object instance)
 370  
         {
 371  
             // do nothing
 372  0
         }
 373  
     }
 374  
 }