Coverage report

  %line %branch
org.apache.commons.jexl.util.introspection.ClassMap$MethodInfo
0% 
0% 

 1  
 /*
 2  
  * Copyright 2001-2002,2004 The Apache Software Foundation.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.apache.commons.jexl.util.introspection;
 18  
 
 19  
 import java.lang.reflect.Method;
 20  
 import java.lang.reflect.Modifier;
 21  
 import java.util.Hashtable;
 22  
 import java.util.Map;
 23  
 
 24  
 /**
 25  
  * Taken from the Velocity tree so we can be self-sufficient
 26  
  * 
 27  
  * A cache of introspection information for a specific class instance. Keys
 28  
  * {@link java.lang.Method} objects by a concatenation of the method name and
 29  
  * the names of classes that make up the parameters.
 30  
  * 
 31  
  * @since 1.0
 32  
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 33  
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
 34  
  * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
 35  
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
 36  
  * @version $Id: ClassMap.java 398459 2006-04-30 23:14:30Z dion $
 37  
  */
 38  
 public class ClassMap {
 39  
     /** represents a miss on the cached data. */
 40  
     private static final class CacheMiss {
 41  
     }
 42  
 
 43  
     /** constant for a miss on the cached data. */
 44  
     private static final CacheMiss CACHE_MISS = new CacheMiss();
 45  
 
 46  
     /** represents null or missing arguments. */
 47  
     private static final Object OBJECT = new Object();
 48  
 
 49  
     /**
 50  
      * Class passed into the constructor used to as the basis for the Method
 51  
      * map.
 52  
      */
 53  
 
 54  
     private Class clazz;
 55  
 
 56  
     /**
 57  
      * Cache of Methods, or CACHE_MISS, keyed by method name and actual
 58  
      * arguments used to find it.
 59  
      */
 60  
     private final Map methodCache = new Hashtable();
 61  
 
 62  
     /** map from method name and args to a {@link Method}. */
 63  
     private final MethodMap methodMap = new MethodMap();
 64  
 
 65  
     /**
 66  
      * Standard constructor.
 67  
      * @param aClass the class to deconstruct. 
 68  
      */
 69  
     public ClassMap(Class aClass) {
 70  
         clazz = aClass;
 71  
         populateMethodCache();
 72  
     }
 73  
 
 74  
     /**
 75  
      * @return the class object whose methods are cached by this map.
 76  
      */
 77  
     Class getCachedClass() {
 78  
         return clazz;
 79  
     }
 80  
 
 81  
     /**
 82  
      * Find a Method using the methodKey provided.
 83  
      * 
 84  
      * Look in the methodMap for an entry. If found, it'll either be a
 85  
      * CACHE_MISS, in which case we simply give up, or it'll be a Method, in
 86  
      * which case, we return it.
 87  
      * 
 88  
      * If nothing is found, then we must actually go and introspect the method
 89  
      * from the MethodMap.
 90  
      * 
 91  
      * @param name method name
 92  
      * @param params method parameters
 93  
      * @return CACHE_MISS or a {@link Method}
 94  
      * @throws MethodMap.AmbiguousException if the method and parameters are ambiguous.
 95  
      */
 96  
     public Method findMethod(String name, Object[] params) throws MethodMap.AmbiguousException {
 97  
         String methodKey = makeMethodKey(name, params);
 98  
         Object cacheEntry = methodCache.get(methodKey);
 99  
 
 100  
         if (cacheEntry == CACHE_MISS) {
 101  
             return null;
 102  
         }
 103  
 
 104  
         if (cacheEntry == null) {
 105  
             try {
 106  
                 cacheEntry = methodMap.find(name, params);
 107  
             } catch (MethodMap.AmbiguousException ae) {
 108  
                 /*
 109  
                  * that's a miss :)
 110  
                  */
 111  
 
 112  
                 methodCache.put(methodKey, CACHE_MISS);
 113  
 
 114  
                 throw ae;
 115  
             }
 116  
 
 117  
             if (cacheEntry == null) {
 118  
                 methodCache.put(methodKey, CACHE_MISS);
 119  
             } else {
 120  
                 methodCache.put(methodKey, cacheEntry);
 121  
             }
 122  
         }
 123  
 
 124  
         // Yes, this might just be null.
 125  
 
 126  
         return (Method) cacheEntry;
 127  
     }
 128  
 
 129  
     /**
 130  
      * Populate the Map of direct hits. These are taken from all the public
 131  
      * methods that our class provides.
 132  
      */
 133  
     private void populateMethodCache() {
 134  
 
 135  
         /*
 136  
          * get all publicly accessible methods
 137  
          */
 138  
 
 139  
         Method[] methods = getAccessibleMethods(clazz);
 140  
 
 141  
         /*
 142  
          * map and cache them
 143  
          */
 144  
 
 145  
         for (int i = 0; i < methods.length; i++) {
 146  
             Method method = methods[i];
 147  
 
 148  
             /*
 149  
              * now get the 'public method', the method declared by a public
 150  
              * interface or class. (because the actual implementing class may be
 151  
              * a facade...
 152  
              */
 153  
 
 154  
             Method publicMethod = getPublicMethod(method);
 155  
 
 156  
             /*
 157  
              * it is entirely possible that there is no public method for the
 158  
              * methods of this class (i.e. in the facade, a method that isn't on
 159  
              * any of the interfaces or superclass in which case, ignore it.
 160  
              * Otherwise, map and cache
 161  
              */
 162  
 
 163  
             if (publicMethod != null) {
 164  
                 methodMap.add(publicMethod);
 165  
                 methodCache.put(makeMethodKey(publicMethod), publicMethod);
 166  
             }
 167  
         }
 168  
     }
 169  
 
 170  
     /**
 171  
      * Make a methodKey for the given method using the concatenation of the name
 172  
      * and the types of the method parameters.
 173  
      */
 174  
     private String makeMethodKey(Method method) {
 175  
         Class[] parameterTypes = method.getParameterTypes();
 176  
 
 177  
         StringBuffer methodKey = new StringBuffer(method.getName());
 178  
 
 179  
         for (int j = 0; j < parameterTypes.length; j++) {
 180  
             /*
 181  
              * If the argument type is primitive then we want to convert our
 182  
              * primitive type signature to the corresponding Object type so
 183  
              * introspection for methods with primitive types will work
 184  
              * correctly.
 185  
              */
 186  
             if (parameterTypes[j].isPrimitive()) {
 187  
                 if (parameterTypes[j].equals(Boolean.TYPE))
 188  
                     methodKey.append("java.lang.Boolean");
 189  
                 else if (parameterTypes[j].equals(Byte.TYPE))
 190  
                     methodKey.append("java.lang.Byte");
 191  
                 else if (parameterTypes[j].equals(Character.TYPE))
 192  
                     methodKey.append("java.lang.Character");
 193  
                 else if (parameterTypes[j].equals(Double.TYPE))
 194  
                     methodKey.append("java.lang.Double");
 195  
                 else if (parameterTypes[j].equals(Float.TYPE))
 196  
                     methodKey.append("java.lang.Float");
 197  
                 else if (parameterTypes[j].equals(Integer.TYPE))
 198  
                     methodKey.append("java.lang.Integer");
 199  
                 else if (parameterTypes[j].equals(Long.TYPE))
 200  
                     methodKey.append("java.lang.Long");
 201  
                 else if (parameterTypes[j].equals(Short.TYPE))
 202  
                     methodKey.append("java.lang.Short");
 203  
             } else {
 204  
                 methodKey.append(parameterTypes[j].getName());
 205  
             }
 206  
         }
 207  
 
 208  
         return methodKey.toString();
 209  
     }
 210  
 
 211  
     private static String makeMethodKey(String method, Object[] params) {
 212  
         StringBuffer methodKey = new StringBuffer().append(method);
 213  
 
 214  
         for (int j = 0; j < params.length; j++) {
 215  
             Object arg = params[j];
 216  
 
 217  
             if (arg == null) {
 218  
                 arg = OBJECT;
 219  
             }
 220  
 
 221  
             methodKey.append(arg.getClass().getName());
 222  
         }
 223  
 
 224  
         return methodKey.toString();
 225  
     }
 226  
 
 227  
     /**
 228  
      * Retrieves public methods for a class. In case the class is not public,
 229  
      * retrieves methods with same signature as its public methods from public
 230  
      * superclasses and interfaces (if they exist). Basically upcasts every
 231  
      * method to the nearest acccessible method.
 232  
      */
 233  
     private static Method[] getAccessibleMethods(Class clazz) {
 234  
         Method[] methods = clazz.getMethods();
 235  
 
 236  
         /*
 237  
          * Short circuit for the (hopefully) majority of cases where the clazz
 238  
          * is public
 239  
          */
 240  
 
 241  
         if (Modclass="keyword">ifier.isPublic(clazz.getModclass="keyword">ifiers())) {
 242  
             return methods;
 243  
         }
 244  
 
 245  
         /*
 246  
          * No luck - the class is not public, so we're going the longer way.
 247  
          */
 248  
 
 249  
         MethodInfo[] methodInfos = new MethodInfo[methods.length];
 250  
 
 251  
         for (int i = methods.length; i-- > 0;) {
 252  
             methodInfos[i] = new MethodInfo(methods[i]);
 253  
         }
 254  
 
 255  
         int upcastCount = getAccessibleMethods(clazz, methodInfos, 0);
 256  
 
 257  
         /*
 258  
          * Reallocate array in case some method had no accessible counterpart.
 259  
          */
 260  
 
 261  
         if (upcastCount < methods.length) {
 262  
             methods = new Method[upcastCount];
 263  
         }
 264  
 
 265  
         int j = 0;
 266  
         for (int i = 0; i < methodInfos.length; ++i) {
 267  
             MethodInfo methodInfo = methodInfos[i];
 268  
             if (methodInfo.upcast) {
 269  
                 methods[j++] = methodInfo.method;
 270  
             }
 271  
         }
 272  
         return methods;
 273  
     }
 274  
 
 275  
     /**
 276  
      * Recursively finds a match for each method, starting with the class, and
 277  
      * then searching the superclass and interfaces.
 278  
      * 
 279  
      * @param clazz Class to check
 280  
      * @param methodInfos array of methods we are searching to match
 281  
      * @param upcastCount current number of methods we have matched
 282  
      * @return count of matched methods
 283  
      */
 284  
     private static int getAccessibleMethods(Class clazz, MethodInfo[] methodInfos, class="keyword">int upcastCount) {
 285  
         int l = methodInfos.length;
 286  
 
 287  
         /*
 288  
          * if this class is public, then check each of the currently
 289  
          * 'non-upcasted' methods to see if we have a match
 290  
          */
 291  
 
 292  
         if (Modclass="keyword">ifier.isPublic(clazz.getModclass="keyword">ifiers())) {
 293  
             for (int i = 0; i < l && upcastCount < l; ++i) {
 294  
                 try {
 295  
                     MethodInfo methodInfo = methodInfos[i];
 296  
 
 297  
                     if (!methodInfo.upcast) {
 298  
                         methodInfo.tryUpcasting(clazz);
 299  
                         upcastCount++;
 300  
                     }
 301  
                 } catch (NoSuchMethodException e) {
 302  
                     /*
 303  
                      * Intentionally ignored - it means it wasn't found in the
 304  
                      * current class
 305  
                      */
 306  
                 }
 307  
             }
 308  
 
 309  
             /*
 310  
              * Short circuit if all methods were upcast
 311  
              */
 312  
 
 313  
             if (upcastCount == l) {
 314  
                 return upcastCount;
 315  
             }
 316  
         }
 317  
 
 318  
         /*
 319  
          * Examine superclass
 320  
          */
 321  
 
 322  
         Class superclazz = clazz.getSuperclass();
 323  
 
 324  
         if (superclazz != null) {
 325  
             upcastCount = getAccessibleMethods(superclazz, methodInfos, upcastCount);
 326  
 
 327  
             /*
 328  
              * Short circuit if all methods were upcast
 329  
              */
 330  
 
 331  
             if (upcastCount == l) {
 332  
                 return upcastCount;
 333  
             }
 334  
         }
 335  
 
 336  
         /*
 337  
          * Examine interfaces. Note we do it even if superclazz == null. This is
 338  
          * redundant as currently java.lang.Object does not implement any
 339  
          * interfaces, however nothing guarantees it will not in future.
 340  
          */
 341  
 
 342  
         Class[] interfaces = clazz.getInterfaces();
 343  
 
 344  
         for (int i = class="keyword">interfaces.length; i-- > 0;) {
 345  
             upcastCount = getAccessibleMethods(interfaces[i], methodInfos, upcastCount);
 346  
 
 347  
             /*
 348  
              * Short circuit if all methods were upcast
 349  
              */
 350  
 
 351  
             if (upcastCount == l) {
 352  
                 return upcastCount;
 353  
             }
 354  
         }
 355  
 
 356  
         return upcastCount;
 357  
     }
 358  
 
 359  
     /**
 360  
      * For a given method, retrieves its publicly accessible counterpart. This
 361  
      * method will look for a method with same name and signature declared in a
 362  
      * public superclass or implemented interface of this method's declaring
 363  
      * class. This counterpart method is publicly callable.
 364  
      * 
 365  
      * @param method a method whose publicly callable counterpart is requested.
 366  
      * @return the publicly callable counterpart method. Note that if the
 367  
      *         parameter method is itself declared by a public class, this
 368  
      *         method is an identity function.
 369  
      */
 370  
     public static Method getPublicMethod(Method method) {
 371  
         Class clazz = method.getDeclaringClass();
 372  
 
 373  
         /*
 374  
          * Short circuit for (hopefully the majority of) cases where the
 375  
          * declaring class is public.
 376  
          */
 377  
 
 378  
         if ((clazz.getModclass="keyword">ifiers() & Modclass="keyword">ifier.PUBLIC) != 0) {
 379  
             return method;
 380  
         }
 381  
 
 382  
         return getPublicMethod(clazz, method.getName(), method.getParameterTypes());
 383  
     }
 384  
 
 385  
     /**
 386  
      * Looks up the method with specified name and signature in the first public
 387  
      * superclass or implemented interface of the class.
 388  
      * 
 389  
      * @param class the class whose method is sought
 390  
      * @param name the name of the method
 391  
      * @param paramTypes the classes of method parameters
 392  
      */
 393  
     private static Method getPublicMethod(Class clazz, String name, Class[] paramTypes) {
 394  
         /*
 395  
          * if this class is public, then try to get it
 396  
          */
 397  
 
 398  
         if ((clazz.getModclass="keyword">ifiers() & Modclass="keyword">ifier.PUBLIC) != 0) {
 399  
             try {
 400  
                 return clazz.getMethod(name, paramTypes);
 401  
             } catch (NoSuchMethodException e) {
 402  
                 /*
 403  
                  * If the class does not have the method, then neither its
 404  
                  * superclass nor any of its interfaces has it so quickly return
 405  
                  * null.
 406  
                  */
 407  
                 return null;
 408  
             }
 409  
         }
 410  
 
 411  
         /*
 412  
          * try the superclass
 413  
          */
 414  
 
 415  
         Class superclazz = clazz.getSuperclass();
 416  
 
 417  
         if (superclazz != null) {
 418  
             Method superclazzMethod = getPublicMethod(superclazz, name, paramTypes);
 419  
 
 420  
             if (superclazzMethod != null) {
 421  
                 return superclazzMethod;
 422  
             }
 423  
         }
 424  
 
 425  
         /*
 426  
          * and interfaces
 427  
          */
 428  
 
 429  
         Class[] interfaces = clazz.getInterfaces();
 430  
 
 431  
         for (int i = 0; i < class="keyword">interfaces.length; ++i) {
 432  
             Method interfaceMethod = getPublicMethod(interfaces[i], name, paramTypes);
 433  
 
 434  
             if (interfaceMethod != null) {
 435  
                 return interfaceMethod;
 436  
             }
 437  
         }
 438  
 
 439  
         return null;
 440  
     }
 441  
 
 442  
     /**
 443  
      * Used for the iterative discovery process for public methods.
 444  
      */
 445  
     private static final class MethodInfo {
 446  
         Method method;
 447  
 
 448  
         String name;
 449  
 
 450  
         Class[] parameterTypes;
 451  
 
 452  
         boolean upcast;
 453  
 
 454  0
         MethodInfo(Method method) {
 455  0
             this.method = null;
 456  0
             name = method.getName();
 457  0
             parameterTypes = method.getParameterTypes();
 458  0
             upcast = false;
 459  0
         }
 460  
 
 461  
         void tryUpcasting(Class clazz) throws NoSuchMethodException {
 462  0
             method = clazz.getMethod(name, parameterTypes);
 463  0
             name = null;
 464  0
             parameterTypes = null;
 465  0
             upcast = true;
 466  0
         }
 467  
     }
 468  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.