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