Coverage Report - org.codehaus.plexus.interpolation.reflection.MethodMap
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodMap
34%
36/105
10%
18/173
11.889
MethodMap$AmbiguousException
0%
0/1
N/A
11.889
 
 1  
 package org.codehaus.plexus.interpolation.reflection;
 2  
 
 3  
 /* ====================================================================
 4  
  *   Copyright 2001-2004 The Apache Software Foundation.
 5  
  *
 6  
  *   Licensed under the Apache License, Version 2.0 (the "License");
 7  
  *   you may not use this file except in compliance with the License.
 8  
  *   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, software
 13  
  *   distributed under the License is distributed on an "AS IS" BASIS,
 14  
  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  *   See the License for the specific language governing permissions and
 16  
  *   limitations under the License.
 17  
  * ====================================================================
 18  
  */
 19  
 
 20  
 import java.lang.reflect.Method;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Hashtable;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 
 28  
 /**
 29  
  * <b>NOTE:</b> This class was copied from plexus-utils, to allow this library
 30  
  * to stand completely self-contained.
 31  
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 32  
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
 33  
  * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
 34  
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
 35  
  * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
 36  
  */
 37  9
 public class MethodMap
 38  
 {
 39  
     private static final int MORE_SPECIFIC = 0;
 40  
     private static final int LESS_SPECIFIC = 1;
 41  
     private static final int INCOMPARABLE = 2;
 42  
 
 43  
     /**
 44  
      * Keep track of all methods with the same name.
 45  
      */
 46  9
     Map<String, List<Method>> methodByNameMap = new Hashtable<String, List<Method>>();
 47  
 
 48  
     /**
 49  
      * Add a method to a list of methods by name.
 50  
      * For a particular class we are keeping track
 51  
      * of all the methods with the same name.
 52  
      * @param method {@link Method}
 53  
      */
 54  
     public void add( Method method )
 55  
     {
 56  359
         String methodName = method.getName();
 57  
 
 58  359
         List<Method> l = get( methodName );
 59  
 
 60  359
         if ( l == null)
 61  
         {
 62  273
             l = new ArrayList<Method>();
 63  273
             methodByNameMap.put( methodName, l );
 64  
         }
 65  
 
 66  359
         l.add( method );
 67  
 
 68  359
     }
 69  
 
 70  
     /**
 71  
      * Return a list of methods with the same name.
 72  
      *
 73  
      * @param key  the key
 74  
      * @return list of methods
 75  
      */
 76  
     public List<Method> get( String key )
 77  
     {
 78  361
         return methodByNameMap.get( key );
 79  
     }
 80  
 
 81  
     /**
 82  
      *  <p>
 83  
      *  Find a method.  Attempts to find the
 84  
      *  most specific applicable method using the
 85  
      *  algorithm described in the JLS section
 86  
      *  15.12.2 (with the exception that it can't
 87  
      *  distinguish a primitive type argument from
 88  
      *  an object type argument, since in reflection
 89  
      *  primitive type arguments are represented by
 90  
      *  their object counterparts, so for an argument of
 91  
      *  type (say) java.lang.Integer, it will not be able
 92  
      *  to decide between a method that takes int and a
 93  
      *  method that takes java.lang.Integer as a parameter.
 94  
      *  </p>
 95  
      *
 96  
      *  <p>
 97  
      *  This turns out to be a relatively rare case
 98  
      *  where this is needed - however, functionality
 99  
      *  like this is needed.
 100  
      *  </p>
 101  
      *
 102  
      *  @param methodName name of method
 103  
      *  @param args the actual arguments with which the method is called
 104  
      *  @return the most specific applicable method, or null if no
 105  
      *  method is applicable.
 106  
      *  @throws AmbiguousException if there is more than one maximally
 107  
      *  specific applicable method
 108  
      */
 109  
     public Method find( String methodName, Object[] args )
 110  
         throws AmbiguousException
 111  
     {
 112  2
         List<Method> methodList = get( methodName );
 113  
 
 114  2
         if ( methodList == null )
 115  
         {
 116  0
             return null;
 117  
         }
 118  
 
 119  2
         int l = args.length;
 120  2
         Class<?>[] classes = new Class[l];
 121  
 
 122  4
         for ( int i = 0; i < l; ++i )
 123  
         {
 124  2
             Object arg = args[i];
 125  
 
 126  
             /*
 127  
              * if we are careful down below, a null argument goes in there
 128  
              * so we can know that the null was passed to the method
 129  
              */
 130  2
             classes[i] = arg == null ? null : arg.getClass();
 131  
         }
 132  
 
 133  2
         return getMostSpecific( methodList, classes );
 134  
     }
 135  
 
 136  
     /**
 137  
      *  simple distinguishable exception, used when
 138  
      *  we run across ambiguous overloading
 139  
      */
 140  0
     public static class AmbiguousException
 141  
         extends Exception
 142  
     {
 143  
     }
 144  
 
 145  
 
 146  
     private static Method getMostSpecific( List<Method> methods, Class<?>[] classes )
 147  
         throws AmbiguousException
 148  
     {
 149  2
         LinkedList<Method> applicables = getApplicables( methods, classes );
 150  
 
 151  2
         if ( applicables.isEmpty() )
 152  
         {
 153  0
             return null;
 154  
         }
 155  
 
 156  2
         if ( applicables.size() == 1 )
 157  
         {
 158  2
             return applicables.getFirst();
 159  
         }
 160  
 
 161  
         /*
 162  
          * This list will contain the maximally specific methods. Hopefully at
 163  
          * the end of the below loop, the list will contain exactly one method,
 164  
          * (the most specific method) otherwise we have ambiguity.
 165  
          */
 166  
 
 167  0
         LinkedList<Method> maximals = new LinkedList<Method>();
 168  
 
 169  0
         for ( Method app : applicables )
 170  
         {
 171  0
             Class<?>[] appArgs = app.getParameterTypes();
 172  0
             boolean lessSpecific = false;
 173  
 
 174  0
             for ( Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); )
 175  
             {
 176  0
                 Method max = maximal.next();
 177  
 
 178  0
                 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  0
                         maximal.remove();
 188  0
                         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  0
                         lessSpecific = true;
 201  
                         break;
 202  
                     }
 203  
                 }
 204  0
             }
 205  
 
 206  0
             if ( !lessSpecific )
 207  
             {
 208  0
                 maximals.addLast( app );
 209  
             }
 210  0
         }
 211  
 
 212  0
         if ( maximals.size() > 1 )
 213  
         {
 214  
             // We have more than one maximally specific method
 215  0
             throw new AmbiguousException();
 216  
         }
 217  
 
 218  0
         return 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  0
         boolean c1MoreSpecific = false;
 232  0
         boolean c2MoreSpecific = false;
 233  
 
 234  0
         for ( int i = 0; i < c1.length; ++i )
 235  
         {
 236  0
             if ( c1[i] != c2[i] )
 237  
             {
 238  0
                 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible( c2[i], c1[i] );
 239  0
                 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible( c1[i], c2[i] );
 240  
             }
 241  
         }
 242  
 
 243  0
         if ( c1MoreSpecific )
 244  
         {
 245  0
             if ( c2MoreSpecific )
 246  
             {
 247  
                 /*
 248  
                  *  Incomparable due to cross-assignable arguments (i.e.
 249  
                  * foo(String, Object) vs. foo(Object, String))
 250  
                  */
 251  
 
 252  0
                 return INCOMPARABLE;
 253  
             }
 254  
 
 255  0
             return MORE_SPECIFIC;
 256  
         }
 257  
 
 258  0
         if ( c2MoreSpecific )
 259  
         {
 260  0
             return LESS_SPECIFIC;
 261  
         }
 262  
 
 263  
         /*
 264  
          * Incomparable due to non-related arguments (i.e.
 265  
          * foo(Runnable) vs. foo(Serializable))
 266  
          */
 267  
 
 268  0
         return INCOMPARABLE;
 269  
     }
 270  
 
 271  
     /**
 272  
      * Returns all methods that are applicable to actual argument types.
 273  
      * @param methods list of all candidate methods
 274  
      * @param classes the actual types of the arguments
 275  
      * @return a list that contains only applicable methods (number of
 276  
      * formal and actual arguments matches, and argument types are assignable
 277  
      * to formal types through a method invocation conversion).
 278  
      */
 279  
     private static LinkedList<Method> getApplicables( List<Method> methods, Class<?>[] classes )
 280  
     {
 281  2
         LinkedList<Method> list = new LinkedList<Method>();
 282  
 
 283  2
         for ( Method method : methods )
 284  
         {
 285  2
             if ( isApplicable( method, classes ) )
 286  
             {
 287  2
                 list.add( method );
 288  
             }
 289  2
         }
 290  2
         return list;
 291  
     }
 292  
 
 293  
     /**
 294  
      * Returns true if the supplied method is applicable to actual
 295  
      * argument types.
 296  
      */
 297  
     private static boolean isApplicable( Method method, Class<?>[] classes )
 298  
     {
 299  2
         Class<?>[] methodArgs = method.getParameterTypes();
 300  
 
 301  2
         if ( methodArgs.length != classes.length )
 302  
         {
 303  0
             return false;
 304  
         }
 305  
 
 306  4
         for ( int i = 0; i < classes.length; ++i )
 307  
         {
 308  2
             if ( !isMethodInvocationConvertible( methodArgs[i], classes[i] ) )
 309  
             {
 310  0
                 return false;
 311  
             }
 312  
         }
 313  
 
 314  2
         return true;
 315  
     }
 316  
 
 317  
     /**
 318  
      * Determines whether a type represented by a class object is
 319  
      * convertible to another type represented by a class object using a
 320  
      * method invocation conversion, treating object types of primitive
 321  
      * types as if they were primitive types (that is, a Boolean actual
 322  
      * parameter type matches boolean primitive formal type). This behavior
 323  
      * is because this method is used to determine applicable methods for
 324  
      * an actual parameter list, and primitive types are represented by
 325  
      * their object duals in reflective method calls.
 326  
      *
 327  
      * @param formal the formal parameter type to which the actual
 328  
      * parameter type should be convertible
 329  
      * @param actual the actual parameter type.
 330  
      * @return true if either formal type is assignable from actual type,
 331  
      * or formal is a primitive type and actual is its corresponding object
 332  
      * type or an object type of a primitive type that can be converted to
 333  
      * the formal type.
 334  
      */
 335  
     private static boolean isMethodInvocationConvertible( Class<?> formal, Class<?> actual )
 336  
     {
 337  
         /*
 338  
          * if it's a null, it means the arg was null
 339  
          */
 340  2
         if ( actual == null && !formal.isPrimitive() )
 341  
         {
 342  0
             return true;
 343  
         }
 344  
 
 345  
         /*
 346  
          *  Check for identity or widening reference conversion
 347  
          */
 348  
 
 349  2
         if ( actual != null && formal.isAssignableFrom( actual ) )
 350  
         {
 351  2
             return true;
 352  
         }
 353  
 
 354  
         /*
 355  
          * Check for boxing with widening primitive conversion. Note that
 356  
          * actual parameters are never primitives.
 357  
          */
 358  
 
 359  0
         if ( formal.isPrimitive() )
 360  
         {
 361  0
             if ( formal == Boolean.TYPE )
 362  
             {
 363  0
                 return actual == Boolean.class;
 364  
             }
 365  0
             if ( formal == Character.TYPE )
 366  
             {
 367  0
                 return actual == Character.class;
 368  
             }
 369  0
             if ( formal == Byte.TYPE )
 370  
             {
 371  0
                 return actual == Byte.class;
 372  
             }
 373  0
             if ( formal == Short.TYPE )
 374  
             {
 375  0
                 return actual == Short.class || actual == Byte.class;
 376  
             }
 377  0
             if ( formal == Integer.TYPE )
 378  
             {
 379  0
                 return actual == Integer.class || actual == Short.class || actual == Byte.class;
 380  
             }
 381  0
             if ( formal == Long.TYPE )
 382  
             {
 383  0
                 return actual == Long.class || actual == Integer.class || actual == Short.class || actual == Byte.class;
 384  
             }
 385  0
             if ( formal == Float.TYPE )
 386  
             {
 387  0
                 return actual == Float.class || actual == Long.class || actual == Integer.class
 388  
                     || actual == Short.class || actual == Byte.class;
 389  
             }
 390  0
             if ( formal == Double.TYPE )
 391  
             {
 392  0
                 return actual == Double.class || actual == Float.class || actual == Long.class
 393  
                     || actual == Integer.class || actual == Short.class || actual == Byte.class;
 394  
             }
 395  
         }
 396  
 
 397  0
         return false;
 398  
     }
 399  
 
 400  
     /**
 401  
      * Determines whether a type represented by a class object is
 402  
      * convertible to another type represented by a class object using a
 403  
      * method invocation conversion, without matching object and primitive
 404  
      * types. This method is used to determine the more specific type when
 405  
      * comparing signatures of methods.
 406  
      *
 407  
      * @param formal the formal parameter type to which the actual
 408  
      * parameter type should be convertible
 409  
      * @param actual the actual parameter type.
 410  
      * @return true if either formal type is assignable from actual type,
 411  
      * or formal and actual are both primitive types and actual can be
 412  
      * subject to widening conversion to formal.
 413  
      */
 414  
     private static boolean isStrictMethodInvocationConvertible( Class<?> formal, Class<?> actual )
 415  
     {
 416  
         /*
 417  
          * we shouldn't get a null into, but if so
 418  
          */
 419  0
         if ( actual == null && !formal.isPrimitive() )
 420  
         {
 421  0
             return true;
 422  
         }
 423  
 
 424  
         /*
 425  
          *  Check for identity or widening reference conversion
 426  
          */
 427  
 
 428  0
         if ( formal.isAssignableFrom( actual ) )
 429  
         {
 430  0
             return true;
 431  
         }
 432  
 
 433  
         /*
 434  
          *  Check for widening primitive conversion.
 435  
          */
 436  
 
 437  0
         if ( formal.isPrimitive() )
 438  
         {
 439  0
             if ( formal == Short.TYPE )
 440  
             {
 441  0
                 return actual == Byte.TYPE;
 442  
             }
 443  0
             if ( formal == Integer.TYPE )
 444  
             {
 445  0
                 return actual == Short.TYPE || actual == Byte.TYPE;
 446  
             }
 447  0
             if ( formal == Long.TYPE )
 448  
             {
 449  0
                 return actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE;
 450  
             }
 451  0
             if ( formal == Float.TYPE )
 452  
             {
 453  0
                 return actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE;
 454  
             }
 455  0
             if ( formal == Double.TYPE )
 456  
             {
 457  0
                 return actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE
 458  
                     || actual == Byte.TYPE;
 459  
             }
 460  
         }
 461  0
         return false;
 462  
     }
 463  
 }