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