Coverage report

  %line %branch
org.apache.commons.jexl.util.introspection.MethodMap$AmbiguousException
0% 
0% 

 1  
 /*
 2  
  * Copyright 2001,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.util.ArrayList;
 21  
 import java.util.Hashtable;
 22  
 import java.util.Iterator;
 23  
 import java.util.LinkedList;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 
 27  
 /**
 28  
  * 
 29  
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 30  
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
 31  
  * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
 32  
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
 33  
  * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
 34  
  * @since 1.0
 35  
  * @version $Id: MethodMap.java 398495 2006-05-01 01:33:43Z dion $
 36  
  */
 37  
 public class MethodMap {
 38  
     /** whether a method is more specific than a previously compared one. */
 39  
     private static final int MORE_SPECIFIC = 0;
 40  
     /** whether a method is less specific than a previously compared one. */
 41  
     private static final int LESS_SPECIFIC = 1;
 42  
     /** A method doesn't match a previously compared one. */
 43  
     private static final int INCOMPARABLE = 2;
 44  
 
 45  
     /**
 46  
      * Keep track of all methods with the same name.
 47  
      */
 48  
     protected Map methodByNameMap = new Hashtable();
 49  
 
 50  
     /**
 51  
      * Add a method to a list of methods by name. For a particular class we are
 52  
      * keeping track of all the methods with the same name.
 53  
      * @param method the method.
 54  
      */
 55  
     public void add(Method method) {
 56  
         String methodName = method.getName();
 57  
 
 58  
         List l = get(methodName);
 59  
 
 60  
         if (l == null) {
 61  
             l = new ArrayList();
 62  
             methodByNameMap.put(methodName, l);
 63  
         }
 64  
 
 65  
         l.add(method);
 66  
     }
 67  
 
 68  
     /**
 69  
      * Return a list of methods with the same name.
 70  
      * 
 71  
      * @param key The method name.
 72  
      * @return List list of methods
 73  
      */
 74  
     public List get(String key) {
 75  
         return (List) methodByNameMap.get(key);
 76  
     }
 77  
 
 78  
     /**
 79  
      * <p>
 80  
      * Find a method. Attempts to find the most specific applicable method using
 81  
      * the algorithm described in the JLS section 15.12.2 (with the exception
 82  
      * that it can't distinguish a primitive type argument from an object type
 83  
      * argument, since in reflection primitive type arguments are represented by
 84  
      * their object counterparts, so for an argument of type (say)
 85  
      * java.lang.Integer, it will not be able to decide between a method that
 86  
      * takes int and a method that takes java.lang.Integer as a parameter.
 87  
      * </p>
 88  
      * 
 89  
      * <p>
 90  
      * This turns out to be a relatively rare case where this is needed -
 91  
      * however, functionality like this is needed.
 92  
      * </p>
 93  
      * 
 94  
      * @param methodName name of method
 95  
      * @param args the actual arguments with which the method is called
 96  
      * @return the most specific applicable method, or null if no method is
 97  
      *         applicable.
 98  
      * @throws AmbiguousException if there is more than one maximally specific
 99  
      *             applicable method
 100  
      */
 101  
     public Method find(String methodName, Object[] args) throws AmbiguousException {
 102  
         List methodList = get(methodName);
 103  
 
 104  
         if (methodList == null) {
 105  
             return null;
 106  
         }
 107  
 
 108  
         int l = args.length;
 109  
         Class[] classes = new Class[l];
 110  
 
 111  
         for (int i = 0; i < l; ++i) {
 112  
             Object arg = args[i];
 113  
 
 114  
             /*
 115  
              * if we are careful down below, a null argument goes in there so we
 116  
              * can know that the null was passed to the method
 117  
              */
 118  
             classes[i] = arg == null ? class="keyword">null : arg.getClass();
 119  
         }
 120  
 
 121  
         return getMostSpecific(methodList, classes);
 122  
     }
 123  
 
 124  
     /**
 125  
      * simple distinguishable exception, used when we run across ambiguous
 126  
      * overloading.
 127  
      */
 128  0
     public static class AmbiguousException extends Exception {
 129  
         /** serialization version id jdk13 generated. */
 130  
         static final long serialVersionUID = 8758118091728717367L;
 131  
     }
 132  
 
 133  
     /**
 134  
      * Gets the most specific method from a list.
 135  
      * @param methods list of {@link Method methods}
 136  
      * @param classes argument types
 137  
      * @return the most specific method, or null
 138  
      * @throws AmbiguousException if there is more than one specific method
 139  
      */
 140  
     private static Method getMostSpecific(List methods, Class[] classes) throws AmbiguousException {
 141  
         LinkedList applicables = getApplicables(methods, classes);
 142  
 
 143  
         if (applicables.isEmpty()) {
 144  
             return null;
 145  
         }
 146  
 
 147  
         if (applicables.size() == 1) {
 148  
             return (Method) applicables.getFirst();
 149  
         }
 150  
 
 151  
         /*
 152  
          * This list will contain the maximally specific methods. Hopefully at
 153  
          * the end of the below loop, the list will contain exactly one method,
 154  
          * (the most specific method) otherwise we have ambiguity.
 155  
          */
 156  
 
 157  
         LinkedList maximals = new LinkedList();
 158  
 
 159  
         for (Iterator applicable = applicables.iterator(); applicable.hasNext();) {
 160  
             Method app = (Method) applicable.next();
 161  
             Class[] appArgs = app.getParameterTypes();
 162  
             boolean lessSpecific = false;
 163  
 
 164  
             for (Iterator maximal = maximals.iterator(); !lessSpecific && maximal.hasNext();) {
 165  
                 Method max = (Method) maximal.next();
 166  
 
 167  
                 switch (moreSpecific(appArgs, max.getParameterTypes())) {
 168  
                     case MORE_SPECIFIC:
 169  
                         /*
 170  
                          * This method is more specific than the previously
 171  
                          * known maximally specific, so remove the old maximum.
 172  
                          */
 173  
                         maximal.remove();
 174  
                         break;
 175  
 
 176  
                     case LESS_SPECIFIC:
 177  
                         /*
 178  
                          * This method is less specific than some of the
 179  
                          * currently known maximally specific methods, so we
 180  
                          * won't add it into the set of maximally specific
 181  
                          * methods
 182  
                          */
 183  
                         lessSpecific = true;
 184  
                         break;
 185  
                 }
 186  
             }
 187  
 
 188  
             if (!lessSpecclass="keyword">ific) {
 189  
                 maximals.addLast(app);
 190  
             }
 191  
         }
 192  
 
 193  
         if (maximals.size() > 1) {
 194  
             // We have more than one maximally specific method
 195  
             throw new AmbiguousException();
 196  
         }
 197  
 
 198  
         return (Method) maximals.getFirst();
 199  
     }
 200  
 
 201  
     /**
 202  
      * Determines which method signature (represented by a class array) is more
 203  
      * specific. This defines a partial ordering on the method signatures.
 204  
      * 
 205  
      * @param c1 first signature to compare
 206  
      * @param c2 second signature to compare
 207  
      * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if c1
 208  
      *         is less specific than c2, INCOMPARABLE if they are incomparable.
 209  
      */
 210  
     private static int moreSpecific(Class[] c1, Class[] c2) {
 211  
         boolean c1MoreSpecific = false;
 212  
         boolean c2MoreSpecific = false;
 213  
 
 214  
         for (int i = 0; i < c1.length; ++i) {
 215  
             if (c1[i] != c2[i]) {
 216  
                 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible(c2[i], c1[i]);
 217  
                 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible(c1[i], c2[i]);
 218  
             }
 219  
         }
 220  
 
 221  
         if (c1MoreSpecclass="keyword">ific) {
 222  
             if (c2MoreSpecclass="keyword">ific) {
 223  
                 /*
 224  
                  * Incomparable due to cross-assignable arguments (i.e.
 225  
                  * foo(String, Object) vs. foo(Object, String))
 226  
                  */
 227  
 
 228  
                 return INCOMPARABLE;
 229  
             }
 230  
 
 231  
             return MORE_SPECIFIC;
 232  
         }
 233  
 
 234  
         if (c2MoreSpecclass="keyword">ific) {
 235  
             return LESS_SPECIFIC;
 236  
         }
 237  
 
 238  
         /*
 239  
          * Incomparable due to non-related arguments (i.e. foo(Runnable) vs.
 240  
          * foo(Serializable))
 241  
          */
 242  
 
 243  
         return INCOMPARABLE;
 244  
     }
 245  
 
 246  
     /**
 247  
      * Returns all methods that are applicable to actual argument types.
 248  
      * 
 249  
      * @param methods list of all candidate methods
 250  
      * @param classes the actual types of the arguments
 251  
      * @return a list that contains only applicable methods (number of formal
 252  
      *         and actual arguments matches, and argument types are assignable
 253  
      *         to formal types through a method invocation conversion).
 254  
      */
 255  
     private static LinkedList getApplicables(List methods, Class[] classes) {
 256  
         LinkedList list = new LinkedList();
 257  
 
 258  
         for (Iterator imethod = methods.iterator(); imethod.hasNext();) {
 259  
             Method method = (Method) imethod.next();
 260  
 
 261  
             if (isApplicable(method, classes)) {
 262  
                 list.add(method);
 263  
             }
 264  
 
 265  
         }
 266  
         return list;
 267  
     }
 268  
 
 269  
     /**
 270  
      * Returns true if the supplied method is applicable to actual argument
 271  
      * types.
 272  
      * @param method the method to check
 273  
      * @param classes possible argument types
 274  
      * @return true if the arguments are applicable to the method.
 275  
      */
 276  
     private static boolean isApplicable(Method method, Class[] classes) {
 277  
         Class[] methodArgs = method.getParameterTypes();
 278  
 
 279  
         if (methodArgs.length != classes.length) {
 280  
             return false;
 281  
         }
 282  
 
 283  
         for (int i = 0; i < classes.length; ++i) {
 284  
             if (!isMethodInvocationConvertible(methodArgs[i], classes[i])) {
 285  
                 return false;
 286  
             }
 287  
         }
 288  
 
 289  
         return true;
 290  
     }
 291  
 
 292  
     /**
 293  
      * Determines whether a type represented by a class object is convertible to
 294  
      * another type represented by a class object using a method invocation
 295  
      * conversion, treating object types of primitive types as if they were
 296  
      * primitive types (that is, a Boolean actual parameter type matches boolean
 297  
      * primitive formal type). This behavior is because this method is used to
 298  
      * determine applicable methods for an actual parameter list, and primitive
 299  
      * types are represented by their object duals in reflective method calls.
 300  
      * 
 301  
      * @param formal the formal parameter type to which the actual parameter
 302  
      *            type should be convertible
 303  
      * @param actual the actual parameter type.
 304  
      * @return true if either formal type is assignable from actual type, or
 305  
      *         formal is a primitive type and actual is its corresponding object
 306  
      *         type or an object type of a primitive type that can be converted
 307  
      *         to the formal type.
 308  
      */
 309  
     private static boolean isMethodInvocationConvertible(Class formal, Class actual) {
 310  
         /*
 311  
          * if it's a null, it means the arg was null
 312  
          */
 313  
         if (actual == null && !formal.isPrimitive()) {
 314  
             return true;
 315  
         }
 316  
 
 317  
         /*
 318  
          * Check for identity or widening reference conversion
 319  
          */
 320  
 
 321  
         if (actual != null && formal.isAssignableFrom(actual)) {
 322  
             return true;
 323  
         }
 324  
 
 325  
         /*
 326  
          * Check for boxing with widening primitive conversion. Note that actual
 327  
          * parameters are never primitives.
 328  
          */
 329  
 
 330  
         if (formal.isPrimitive()) {
 331  
             if (formal == Boolean.TYPE && actual == Boolean.class) {
 332  
                 return true;
 333  
             }
 334  
             if (formal == Character.TYPE && actual == Character.class) {
 335  
                 return true;
 336  
             }
 337  
             if (formal == Byte.TYPE && actual == Byte.class) {
 338  
                 return true;
 339  
             }
 340  
             if (formal == Short.TYPE && (actual == Short.class || actual == Byte.class)) {
 341  
                 return true;
 342  
             }
 343  
             if (formal == Integer.TYPE && (actual == Integer.class || actual == Short.class || actual == Byte.class)) {
 344  
                 return true;
 345  
             }
 346  
             if (formal == Long.TYPE
 347  
                 && (actual == Long.class || actual == Integer.class || actual == Short.class || actual == Byte.class)) {
 348  
                 return true;
 349  
             }
 350  
             if (formal == Float.TYPE
 351  
                 && (actual == Float.class || actual == Long.class || actual == Integer.class 
 352  
                     || actual == Short.class || actual == Byte.class)) {
 353  
                 return true;
 354  
             }
 355  
             if (formal == Double.TYPE
 356  
                 && (actual == Double.class || actual == Float.class || actual == Long.class || actual == Integer.class
 357  
                     || actual == Short.class || actual == Byte.class)) {
 358  
                 return true;
 359  
             }
 360  
         }
 361  
 
 362  
         return false;
 363  
     }
 364  
 
 365  
     /**
 366  
      * Determines whether a type represented by a class object is convertible to
 367  
      * another type represented by a class object using a method invocation
 368  
      * conversion, without matching object and primitive types. This method is
 369  
      * used to determine the more specific type when comparing signatures of
 370  
      * methods.
 371  
      * 
 372  
      * @param formal the formal parameter type to which the actual parameter
 373  
      *            type should be convertible
 374  
      * @param actual the actual parameter type.
 375  
      * @return true if either formal type is assignable from actual type, or
 376  
      *         formal and actual are both primitive types and actual can be
 377  
      *         subject to widening conversion to formal.
 378  
      */
 379  
     private static boolean isStrictMethodInvocationConvertible(Class formal, Class actual) {
 380  
         /*
 381  
          * we shouldn't get a null into, but if so
 382  
          */
 383  
         if (actual == null && !formal.isPrimitive()) {
 384  
             return true;
 385  
         }
 386  
 
 387  
         /*
 388  
          * Check for identity or widening reference conversion
 389  
          */
 390  
 
 391  
         if (formal.isAssignableFrom(actual)) {
 392  
             return true;
 393  
         }
 394  
 
 395  
         /*
 396  
          * Check for widening primitive conversion.
 397  
          */
 398  
 
 399  
         if (formal.isPrimitive()) {
 400  
             if (formal == Short.TYPE && (actual == Byte.TYPE)) {
 401  
                 return true;
 402  
             }
 403  
             if (formal == Integer.TYPE && (actual == Short.TYPE || actual == Byte.TYPE)) {
 404  
                 return true;
 405  
             }
 406  
             if (formal == Long.TYPE && (actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
 407  
                 return true;
 408  
             }
 409  
             if (formal == Float.TYPE
 410  
                 && (actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
 411  
                 return true;
 412  
             }
 413  
             if (formal == Double.TYPE
 414  
                 && (actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE 
 415  
                     || actual == Byte.TYPE)) {
 416  
                 return true;
 417  
             }
 418  
         }
 419  
         return false;
 420  
     }
 421  
 }

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