package org.codehaus.plexus.interpolation.reflection;

/* ====================================================================
 *   Copyright 2001-2004 The Apache Software Foundation.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * ====================================================================
 */

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

NOTE: This class was copied from plexus-utils, to allow this library to stand completely self-contained.
Author:Jason van Zyl, Bob McWhirter, Christoph Reck, Geir Magnusson Jr., Attila Szegedi
/** * <b>NOTE:</b> This class was copied from plexus-utils, to allow this library * to stand completely self-contained. * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> * @author <a href="mailto:bob@werken.com">Bob McWhirter</a> * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a> * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a> */
public class MethodMap { private static final int MORE_SPECIFIC = 0; private static final int LESS_SPECIFIC = 1; private static final int INCOMPARABLE = 2;
Keep track of all methods with the same name.
/** * Keep track of all methods with the same name. */
Map<String, List<Method>> methodByNameMap = new Hashtable<String, List<Method>>();
Add a method to a list of methods by name. For a particular class we are keeping track of all the methods with the same name.
Params:
/** * Add a method to a list of methods by name. * For a particular class we are keeping track * of all the methods with the same name. * @param method {@link Method} */
public void add( Method method ) { String methodName = method.getName(); List<Method> l = get( methodName ); if ( l == null) { l = new ArrayList<Method>(); methodByNameMap.put( methodName, l ); } l.add( method ); }
Return a list of methods with the same name.
Params:
  • key – the key
Returns:list of methods
/** * Return a list of methods with the same name. * * @param key the key * @return list of methods */
public List<Method> get( String key ) { return methodByNameMap.get( key ); }

Find a method. Attempts to find the most specific applicable method using the algorithm described in the JLS section 15.12.2 (with the exception that it can't distinguish a primitive type argument from an object type argument, since in reflection primitive type arguments are represented by their object counterparts, so for an argument of type (say) java.lang.Integer, it will not be able to decide between a method that takes int and a method that takes java.lang.Integer as a parameter.

This turns out to be a relatively rare case where this is needed - however, functionality like this is needed.

@param methodName name of method @param args the actual arguments with which the method is called @return the most specific applicable method, or null if no method is applicable. @throws AmbiguousException if there is more than one maximally specific applicable method
/** * <p> * Find a method. Attempts to find the * most specific applicable method using the * algorithm described in the JLS section * 15.12.2 (with the exception that it can't * distinguish a primitive type argument from * an object type argument, since in reflection * primitive type arguments are represented by * their object counterparts, so for an argument of * type (say) java.lang.Integer, it will not be able * to decide between a method that takes int and a * method that takes java.lang.Integer as a parameter. * </p> * * <p> * This turns out to be a relatively rare case * where this is needed - however, functionality * like this is needed. * </p> * * @param methodName name of method * @param args the actual arguments with which the method is called * @return the most specific applicable method, or null if no * method is applicable. * @throws AmbiguousException if there is more than one maximally * specific applicable method */
public Method find( String methodName, Object[] args ) throws AmbiguousException { List<Method> methodList = get( methodName ); if ( methodList == null ) { return null; } int l = args.length; Class<?>[] classes = new Class[l]; for ( int i = 0; i < l; ++i ) { Object arg = args[i]; /* * if we are careful down below, a null argument goes in there * so we can know that the null was passed to the method */ classes[i] = arg == null ? null : arg.getClass(); } return getMostSpecific( methodList, classes ); }
simple distinguishable exception, used when we run across ambiguous overloading
/** * simple distinguishable exception, used when * we run across ambiguous overloading */
public static class AmbiguousException extends Exception { } private static Method getMostSpecific( List<Method> methods, Class<?>[] classes ) throws AmbiguousException { LinkedList<Method> applicables = getApplicables( methods, classes ); if ( applicables.isEmpty() ) { return null; } if ( applicables.size() == 1 ) { return applicables.getFirst(); } /* * This list will contain the maximally specific methods. Hopefully at * the end of the below loop, the list will contain exactly one method, * (the most specific method) otherwise we have ambiguity. */ LinkedList<Method> maximals = new LinkedList<Method>(); for ( Method app : applicables ) { Class<?>[] appArgs = app.getParameterTypes(); boolean lessSpecific = false; for ( Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); ) { Method max = maximal.next(); switch ( moreSpecific( appArgs, max.getParameterTypes() ) ) { case MORE_SPECIFIC: { /* * This method is more specific than the previously * known maximally specific, so remove the old maximum. */ maximal.remove(); break; } case LESS_SPECIFIC: { /* * This method is less specific than some of the * currently known maximally specific methods, so we * won't add it into the set of maximally specific * methods */ lessSpecific = true; break; } } } if ( !lessSpecific ) { maximals.addLast( app ); } } if ( maximals.size() > 1 ) { // We have more than one maximally specific method throw new AmbiguousException(); } return maximals.getFirst(); }
Determines which method signature (represented by a class array) is more specific. This defines a partial ordering on the method signatures.
Params:
  • c1 – first signature to compare
  • c2 – second signature to compare
Returns:MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if c1 is less specific than c2, INCOMPARABLE if they are incomparable.
/** * Determines which method signature (represented by a class array) is more * specific. This defines a partial ordering on the method signatures. * @param c1 first signature to compare * @param c2 second signature to compare * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if * c1 is less specific than c2, INCOMPARABLE if they are incomparable. */
private static int moreSpecific( Class<?>[] c1, Class<?>[] c2 ) { boolean c1MoreSpecific = false; boolean c2MoreSpecific = false; for ( int i = 0; i < c1.length; ++i ) { if ( c1[i] != c2[i] ) { c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible( c2[i], c1[i] ); c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible( c1[i], c2[i] ); } } if ( c1MoreSpecific ) { if ( c2MoreSpecific ) { /* * Incomparable due to cross-assignable arguments (i.e. * foo(String, Object) vs. foo(Object, String)) */ return INCOMPARABLE; } return MORE_SPECIFIC; } if ( c2MoreSpecific ) { return LESS_SPECIFIC; } /* * Incomparable due to non-related arguments (i.e. * foo(Runnable) vs. foo(Serializable)) */ return INCOMPARABLE; }
Returns all methods that are applicable to actual argument types.
Params:
  • methods – list of all candidate methods
  • classes – the actual types of the arguments
Returns:a list that contains only applicable methods (number of formal and actual arguments matches, and argument types are assignable to formal types through a method invocation conversion).
/** * Returns all methods that are applicable to actual argument types. * @param methods list of all candidate methods * @param classes the actual types of the arguments * @return a list that contains only applicable methods (number of * formal and actual arguments matches, and argument types are assignable * to formal types through a method invocation conversion). */
private static LinkedList<Method> getApplicables( List<Method> methods, Class<?>[] classes ) { LinkedList<Method> list = new LinkedList<Method>(); for ( Method method : methods ) { if ( isApplicable( method, classes ) ) { list.add( method ); } } return list; }
Returns true if the supplied method is applicable to actual argument types.
/** * Returns true if the supplied method is applicable to actual * argument types. */
private static boolean isApplicable( Method method, Class<?>[] classes ) { Class<?>[] methodArgs = method.getParameterTypes(); if ( methodArgs.length != classes.length ) { return false; } for ( int i = 0; i < classes.length; ++i ) { if ( !isMethodInvocationConvertible( methodArgs[i], classes[i] ) ) { return false; } } return true; }
Determines whether a type represented by a class object is convertible to another type represented by a class object using a method invocation conversion, treating object types of primitive types as if they were primitive types (that is, a Boolean actual parameter type matches boolean primitive formal type). This behavior is because this method is used to determine applicable methods for an actual parameter list, and primitive types are represented by their object duals in reflective method calls.
Params:
  • formal – the formal parameter type to which the actual parameter type should be convertible
  • actual – the actual parameter type.
Returns:true if either formal type is assignable from actual type, or formal is a primitive type and actual is its corresponding object type or an object type of a primitive type that can be converted to the formal type.
/** * Determines whether a type represented by a class object is * convertible to another type represented by a class object using a * method invocation conversion, treating object types of primitive * types as if they were primitive types (that is, a Boolean actual * parameter type matches boolean primitive formal type). This behavior * is because this method is used to determine applicable methods for * an actual parameter list, and primitive types are represented by * their object duals in reflective method calls. * * @param formal the formal parameter type to which the actual * parameter type should be convertible * @param actual the actual parameter type. * @return true if either formal type is assignable from actual type, * or formal is a primitive type and actual is its corresponding object * type or an object type of a primitive type that can be converted to * the formal type. */
private static boolean isMethodInvocationConvertible( Class<?> formal, Class<?> actual ) { /* * if it's a null, it means the arg was null */ if ( actual == null && !formal.isPrimitive() ) { return true; } /* * Check for identity or widening reference conversion */ if ( actual != null && formal.isAssignableFrom( actual ) ) { return true; } /* * Check for boxing with widening primitive conversion. Note that * actual parameters are never primitives. */ if ( formal.isPrimitive() ) { if ( formal == Boolean.TYPE ) { return actual == Boolean.class; } if ( formal == Character.TYPE ) { return actual == Character.class; } if ( formal == Byte.TYPE ) { return actual == Byte.class; } if ( formal == Short.TYPE ) { return actual == Short.class || actual == Byte.class; } if ( formal == Integer.TYPE ) { return actual == Integer.class || actual == Short.class || actual == Byte.class; } if ( formal == Long.TYPE ) { return actual == Long.class || actual == Integer.class || actual == Short.class || actual == Byte.class; } if ( formal == Float.TYPE ) { return actual == Float.class || actual == Long.class || actual == Integer.class || actual == Short.class || actual == Byte.class; } if ( formal == Double.TYPE ) { return actual == Double.class || actual == Float.class || actual == Long.class || actual == Integer.class || actual == Short.class || actual == Byte.class; } } return false; }
Determines whether a type represented by a class object is convertible to another type represented by a class object using a method invocation conversion, without matching object and primitive types. This method is used to determine the more specific type when comparing signatures of methods.
Params:
  • formal – the formal parameter type to which the actual parameter type should be convertible
  • actual – the actual parameter type.
Returns:true if either formal type is assignable from actual type, or formal and actual are both primitive types and actual can be subject to widening conversion to formal.
/** * Determines whether a type represented by a class object is * convertible to another type represented by a class object using a * method invocation conversion, without matching object and primitive * types. This method is used to determine the more specific type when * comparing signatures of methods. * * @param formal the formal parameter type to which the actual * parameter type should be convertible * @param actual the actual parameter type. * @return true if either formal type is assignable from actual type, * or formal and actual are both primitive types and actual can be * subject to widening conversion to formal. */
private static boolean isStrictMethodInvocationConvertible( Class<?> formal, Class<?> actual ) { /* * we shouldn't get a null into, but if so */ if ( actual == null && !formal.isPrimitive() ) { return true; } /* * Check for identity or widening reference conversion */ if ( formal.isAssignableFrom( actual ) ) { return true; } /* * Check for widening primitive conversion. */ if ( formal.isPrimitive() ) { if ( formal == Short.TYPE ) { return actual == Byte.TYPE; } if ( formal == Integer.TYPE ) { return actual == Short.TYPE || actual == Byte.TYPE; } if ( formal == Long.TYPE ) { return actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE; } if ( formal == Float.TYPE ) { return actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE; } if ( formal == Double.TYPE ) { return actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE; } } return false; } }