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