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

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