Adapted code from Paul Holser for looking up a reflected method that
matches a certain set of argument types that works the compiler works. git-svn-id: https://samskivert.googlecode.com/svn/trunk@335 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,300 @@
|
|||||||
|
//
|
||||||
|
// $Id: ClassUtil.java,v 1.1 2001/10/03 02:09:12 mdb Exp $
|
||||||
|
//
|
||||||
|
// samskivert library - useful routines for java programs
|
||||||
|
// Copyright (C) 2001 Michael Bayne
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.samskivert.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class object related utility routines.
|
||||||
|
*
|
||||||
|
* <p> This code was adapted from code provided by Paul Hosler in an
|
||||||
|
* article for Java Report Online
|
||||||
|
* (http://www.javareport.com/html/from_pages/article.asp?id=4276). His
|
||||||
|
* code was adapted from:
|
||||||
|
*
|
||||||
|
* <cite>
|
||||||
|
* Kaplan, Lowell. "More programming with generic interfaces." Java
|
||||||
|
* Report, Vol. 5, No. 2, Feb. 2000, pp. 70-74.
|
||||||
|
* </cite>
|
||||||
|
*/
|
||||||
|
public class ClassUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param clazz a class.
|
||||||
|
*
|
||||||
|
* @return true if the class is accessible, false otherwise.
|
||||||
|
* Presently returns true if the class is declared public.
|
||||||
|
*/
|
||||||
|
public static boolean classIsAccessible (Class clazz)
|
||||||
|
{
|
||||||
|
return Modifier.isPublic(clazz.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param args an object array.
|
||||||
|
*
|
||||||
|
* @return an array of Class objects representing the classes of the
|
||||||
|
* objects in the given Object array. If args is null, a zero-length
|
||||||
|
* Class array is returned. If an element in args is null, then
|
||||||
|
* {@link Void#TYPE} is the corresponding Class in the return array.
|
||||||
|
*/
|
||||||
|
public static Class[] getParameterTypesFrom (Object[] args)
|
||||||
|
{
|
||||||
|
Class[] argTypes = null;
|
||||||
|
|
||||||
|
if (args != null) {
|
||||||
|
argTypes = new Class[args.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length; ++i) {
|
||||||
|
argTypes[i] = (args[i] == null) ?
|
||||||
|
Void.TYPE : args[i].getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
argTypes = new Class[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return argTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether instances of the classes in the 'rhs' array could be
|
||||||
|
* used as parameters to a reflective method invocation whose
|
||||||
|
* parameter list has types denoted by the 'lhs' array.
|
||||||
|
*
|
||||||
|
* @param lhs Class array representing the types of the formal
|
||||||
|
* parameters of a method.
|
||||||
|
* @param rhs Class array representing the types of the actual
|
||||||
|
* parameters of a method. A null value or Void.TYPE is considered to
|
||||||
|
* match a corresponding Object or array class in lhs, but not a
|
||||||
|
* primitive.
|
||||||
|
*
|
||||||
|
* @return true if compatible, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean compatibleClasses (Class[] lhs, Class[] rhs)
|
||||||
|
{
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < lhs.length; ++i) {
|
||||||
|
if (rhs[i] == null || rhs[i].equals(Void.TYPE)) {
|
||||||
|
if (lhs[i].isPrimitive())
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! lhs[i].isAssignableFrom(rhs[i])) {
|
||||||
|
Class lhsPrimEquiv = primitiveEquivalentOf(lhs[i]);
|
||||||
|
Class rhsPrimEquiv = primitiveEquivalentOf(rhs[i]);
|
||||||
|
|
||||||
|
if (! primitiveIsAssignableFrom(lhsPrimEquiv, rhsPrimEquiv))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for the method with the given name and formal parameter
|
||||||
|
* types that is in the nearest accessible class in the class
|
||||||
|
* hierarchy, starting with clazz's superclass. The superclass and
|
||||||
|
* implemented interfaces of clazz are searched, then their
|
||||||
|
* superclasses, etc. until a method is found. Returns null if there
|
||||||
|
* is no such method.
|
||||||
|
*
|
||||||
|
* @param clazz a class.
|
||||||
|
* @param methodName name of a method.
|
||||||
|
* @param paramTypes Class array representing the types of a method's
|
||||||
|
* formal parameters.
|
||||||
|
*
|
||||||
|
* @return the nearest method located, or null if there is no such
|
||||||
|
* method.
|
||||||
|
*/
|
||||||
|
public static Method getAccessibleMethodFrom (
|
||||||
|
Class clazz, String methodName, Class[] parameterTypes)
|
||||||
|
{
|
||||||
|
// Look for overridden method in the superclass.
|
||||||
|
Class superclass = clazz.getSuperclass();
|
||||||
|
Method overriddenMethod = null;
|
||||||
|
|
||||||
|
if (superclass != null && classIsAccessible(superclass)) {
|
||||||
|
try {
|
||||||
|
overriddenMethod =
|
||||||
|
superclass.getMethod(methodName, parameterTypes);
|
||||||
|
} catch (NoSuchMethodException _) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overriddenMethod != null) {
|
||||||
|
return overriddenMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If here, then clazz represents Object, or an interface, or the
|
||||||
|
// superclass did not have an override. Check implemented
|
||||||
|
// interfaces.
|
||||||
|
Class[] interfaces = clazz.getInterfaces();
|
||||||
|
|
||||||
|
for (int i = 0; i < interfaces.length; ++i) {
|
||||||
|
overriddenMethod = null;
|
||||||
|
|
||||||
|
if (classIsAccessible(interfaces[i])) {
|
||||||
|
try {
|
||||||
|
overriddenMethod =
|
||||||
|
interfaces[i].getMethod(methodName, parameterTypes);
|
||||||
|
} catch (NoSuchMethodException _) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overriddenMethod != null) {
|
||||||
|
return overriddenMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overriddenMethod = null;
|
||||||
|
|
||||||
|
// Try superclass's superclass and implemented interfaces.
|
||||||
|
if (superclass != null) {
|
||||||
|
overriddenMethod = getAccessibleMethodFrom(
|
||||||
|
superclass, methodName, parameterTypes);
|
||||||
|
|
||||||
|
if (overriddenMethod != null) {
|
||||||
|
return overriddenMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try implemented interfaces' extended interfaces...
|
||||||
|
for (int i = 0; i < interfaces.length; ++i) {
|
||||||
|
overriddenMethod = getAccessibleMethodFrom(
|
||||||
|
interfaces[i], methodName, parameterTypes);
|
||||||
|
|
||||||
|
if (overriddenMethod != null) {
|
||||||
|
return overriddenMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give up.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param clazz a Class.
|
||||||
|
*
|
||||||
|
* @return the class's primitive equivalent, if clazz is a primitive
|
||||||
|
* wrapper. If clazz is primitive, returns clazz. Otherwise,
|
||||||
|
* returns null.
|
||||||
|
*/
|
||||||
|
public static Class primitiveEquivalentOf (Class clazz)
|
||||||
|
{
|
||||||
|
return clazz.isPrimitive() ?
|
||||||
|
clazz : (Class)_objectToPrimitiveMap.get(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether an instance of the primitive class represented by
|
||||||
|
* 'rhs' can be assigned to an instance of the primitive class
|
||||||
|
* represented by 'lhs'.
|
||||||
|
*
|
||||||
|
* @param lhs assignee class.
|
||||||
|
* @param rhs assigned class.
|
||||||
|
*
|
||||||
|
* @return true if compatible, false otherwise. If either argument is
|
||||||
|
* <code>null</code>, or one of the parameters does not represent a
|
||||||
|
* primitive (e.g. Byte.TYPE), returns false.
|
||||||
|
*/
|
||||||
|
public static boolean primitiveIsAssignableFrom (Class lhs, Class rhs)
|
||||||
|
{
|
||||||
|
if (lhs == null || rhs == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (! (lhs.isPrimitive() && rhs.isPrimitive()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lhs.equals(rhs))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Set wideningSet = (Set)_primitiveWideningsMap.get(rhs);
|
||||||
|
if (wideningSet == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return wideningSet.contains(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from primitive wrapper Classes to their corresponding
|
||||||
|
* primitive Classes.
|
||||||
|
*/
|
||||||
|
private static final Map _objectToPrimitiveMap = new HashMap(13);
|
||||||
|
|
||||||
|
static {
|
||||||
|
_objectToPrimitiveMap.put(Boolean.class, Boolean.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Byte.class, Byte.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Character.class, Character.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Double.class, Double.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Float.class, Float.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Integer.class, Integer.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Long.class, Long.TYPE);
|
||||||
|
_objectToPrimitiveMap.put(Short.class, Short.TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from primitive wrapper Classes to the sets of primitive
|
||||||
|
* classes whose instances can be assigned an instance of the first.
|
||||||
|
*/
|
||||||
|
private static final Map _primitiveWideningsMap = new HashMap(11);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set set = new HashSet();
|
||||||
|
set.add(Short.TYPE);
|
||||||
|
set.add(Integer.TYPE);
|
||||||
|
set.add(Long.TYPE);
|
||||||
|
set.add(Float.TYPE);
|
||||||
|
set.add(Double.TYPE);
|
||||||
|
_primitiveWideningsMap.put(Byte.TYPE, set);
|
||||||
|
|
||||||
|
set = new HashSet();
|
||||||
|
set.add(Integer.TYPE);
|
||||||
|
set.add(Long.TYPE);
|
||||||
|
set.add(Float.TYPE);
|
||||||
|
set.add(Double.TYPE);
|
||||||
|
_primitiveWideningsMap.put(Short.TYPE, set);
|
||||||
|
_primitiveWideningsMap.put(Character.TYPE, set);
|
||||||
|
|
||||||
|
set = new HashSet();
|
||||||
|
set.add(Long.TYPE);
|
||||||
|
set.add(Float.TYPE);
|
||||||
|
set.add(Double.TYPE);
|
||||||
|
_primitiveWideningsMap.put(Integer.TYPE, set);
|
||||||
|
|
||||||
|
set = new HashSet();
|
||||||
|
set.add(Float.TYPE);
|
||||||
|
set.add(Double.TYPE);
|
||||||
|
_primitiveWideningsMap.put(Long.TYPE, set);
|
||||||
|
|
||||||
|
set = new HashSet();
|
||||||
|
set.add(Double.TYPE);
|
||||||
|
_primitiveWideningsMap.put(Float.TYPE, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,365 @@
|
|||||||
|
//
|
||||||
|
// $Id: MethodFinder.java,v 1.1 2001/10/03 02:09:12 mdb Exp $
|
||||||
|
//
|
||||||
|
// samskivert library - useful routines for java programs
|
||||||
|
// Copyright (C) 2001 Michael Bayne
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.samskivert.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds methods and constructors that can be invoked reflectively.
|
||||||
|
* Attempts to address some of the limitations of the JDK's {@link
|
||||||
|
* Class#getMethod} and {@link Class#getConstructor}, and other JDK
|
||||||
|
* reflective facilities.
|
||||||
|
*
|
||||||
|
* <p> Because those methods only match exact method signatures, one is
|
||||||
|
* unable to perform the same method matching that the compiler does at
|
||||||
|
* compile time (e.g. matching the method <code>foo(Exception)</code> when
|
||||||
|
* the user wants to call a method named <code>foo</code> with an
|
||||||
|
* <code>IOException</code> argument) with the basic reflection services.
|
||||||
|
* This class implements the method resolution process according to the
|
||||||
|
* same rules used by a Java compiler. These rules are outlined in the
|
||||||
|
* Java Language Specification, variously in sections 5.1.2, 5.1.4, 5.3,
|
||||||
|
* and 15.12.2.
|
||||||
|
*
|
||||||
|
* <p> This code was adapted from code provided by Paul Hosler in an
|
||||||
|
* article for Java Report Online
|
||||||
|
* (http://www.javareport.com/html/from_pages/article.asp?id=4276). His
|
||||||
|
* code was adapted from:
|
||||||
|
*
|
||||||
|
* <cite>
|
||||||
|
* Kaplan, Lowell. "More programming with generic interfaces." Java
|
||||||
|
* Report, Vol. 5, No. 2, Feb. 2000, pp. 70-74.
|
||||||
|
* </cite>
|
||||||
|
*/
|
||||||
|
public final class MethodFinder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs a method finder for the supplied class.
|
||||||
|
*
|
||||||
|
* @param clazz Class in which I will look for methods and
|
||||||
|
* constructors.
|
||||||
|
*
|
||||||
|
* @exception IllegalArgumentException if clazz is null, or represents
|
||||||
|
* a primitive, or represents an array type.
|
||||||
|
*/
|
||||||
|
public MethodFinder (Class clazz)
|
||||||
|
{
|
||||||
|
if (clazz == null) {
|
||||||
|
throw new IllegalArgumentException("null Class parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clazz.isPrimitive()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"primitive Class parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clazz.isArray()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"array Class parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals (Object o)
|
||||||
|
{
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
else if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
else {
|
||||||
|
MethodFinder other = (MethodFinder) o;
|
||||||
|
return clazz.equals(other.clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most specific public constructor in my target class
|
||||||
|
* that accepts the number and type of parameters in the given Class
|
||||||
|
* array in a reflective invocation.
|
||||||
|
*
|
||||||
|
* <p> A null value or {@link Void#TYPE} in parameterTypes matches a
|
||||||
|
* corresponding Object or array reference in a constructor's formal
|
||||||
|
* parameter list, but not a primitive formal parameter.
|
||||||
|
*
|
||||||
|
* @param parameterTypes array representing the number and types of
|
||||||
|
* parameters to look for in the constructor's signature. A null
|
||||||
|
* array is treated as a zero-length array.
|
||||||
|
*
|
||||||
|
* @return Constructor object satisfying the conditions.
|
||||||
|
*
|
||||||
|
* @exception NoSuchMethodException if no constructors match the
|
||||||
|
* criteria, or if the reflective call is ambiguous based on the
|
||||||
|
* parameter types.
|
||||||
|
*/
|
||||||
|
public Constructor findConstructor (Class[] parameterTypes)
|
||||||
|
throws NoSuchMethodException
|
||||||
|
{
|
||||||
|
// make sure the constructor list is loaded
|
||||||
|
maybeLoadConstructors();
|
||||||
|
|
||||||
|
if (parameterTypes == null) {
|
||||||
|
parameterTypes = new Class[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Constructor) findMemberIn(ctorList, parameterTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most specific public method in my target class that has
|
||||||
|
* the given name and accepts the number and type of parameters in the
|
||||||
|
* given Class array in a reflective invocation.
|
||||||
|
*
|
||||||
|
* <p> A null value or {@link Void#TYPE} in parameterTypes will match
|
||||||
|
* a corresponding Object or array reference in a method's formal
|
||||||
|
* parameter list, but not a primitive formal parameter.
|
||||||
|
*
|
||||||
|
* @param methodName name of the method to search for.
|
||||||
|
* @param parameterTypes array representing the number and types of
|
||||||
|
* parameters to look for in the method's signature. A null array is
|
||||||
|
* treated as a zero-length array.
|
||||||
|
*
|
||||||
|
* @return Method object satisfying the conditions.
|
||||||
|
*
|
||||||
|
* @exception NoSuchMethodException if no methods match the criteria,
|
||||||
|
* or if the reflective call is ambiguous based on the parameter
|
||||||
|
* types, or if methodName is null.
|
||||||
|
*/
|
||||||
|
public Method findMethod (String methodName, Class[] parameterTypes)
|
||||||
|
throws NoSuchMethodException
|
||||||
|
{
|
||||||
|
// make sure the constructor list is loaded
|
||||||
|
maybeLoadMethods();
|
||||||
|
|
||||||
|
List methodList = (List) methodMap.get(methodName);
|
||||||
|
if (methodList == null) {
|
||||||
|
throw new NoSuchMethodException(
|
||||||
|
"No method named " + clazz.getName() + "." + methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameterTypes == null) {
|
||||||
|
parameterTypes = new Class[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Method) findMemberIn(methodList, parameterTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basis of {@link #findConstructor} and {@link #findMethod}. The
|
||||||
|
* member list fed to this method will be either all {@link
|
||||||
|
* Constructor} objects or all {@link Method} objects.
|
||||||
|
*/
|
||||||
|
private Member findMemberIn (List memberList, Class[] parameterTypes)
|
||||||
|
throws NoSuchMethodException
|
||||||
|
{
|
||||||
|
List matchingMembers = new ArrayList();
|
||||||
|
|
||||||
|
for (Iterator it = memberList.iterator(); it.hasNext();) {
|
||||||
|
Member member = (Member) it.next();
|
||||||
|
Class[] methodParamTypes = (Class[]) paramMap.get(member);
|
||||||
|
|
||||||
|
// check for exactly equal method signature
|
||||||
|
if (Arrays.equals(methodParamTypes, parameterTypes)) {
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClassUtil.compatibleClasses(
|
||||||
|
methodParamTypes, parameterTypes)) {
|
||||||
|
matchingMembers.add(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingMembers.isEmpty()) {
|
||||||
|
throw new NoSuchMethodException(
|
||||||
|
"No member in " + clazz.getName() + " matching given args");
|
||||||
|
}
|
||||||
|
if (matchingMembers.size() == 1) {
|
||||||
|
return (Member) matchingMembers.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return findMostSpecificMemberIn(matchingMembers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param memberList a list of members (either all constructors or all
|
||||||
|
* methods).
|
||||||
|
*
|
||||||
|
* @return the most specific of all members in the list.
|
||||||
|
*
|
||||||
|
* @exception NoSuchMethodException if there is an ambiguity as to
|
||||||
|
* which is most specific.
|
||||||
|
*/
|
||||||
|
private Member findMostSpecificMemberIn (List memberList)
|
||||||
|
throws NoSuchMethodException
|
||||||
|
{
|
||||||
|
List mostSpecificMembers = new ArrayList();
|
||||||
|
|
||||||
|
for (Iterator memberIt = memberList.iterator();
|
||||||
|
memberIt.hasNext();) {
|
||||||
|
Member member = (Member) memberIt.next();
|
||||||
|
|
||||||
|
if (mostSpecificMembers.isEmpty()) {
|
||||||
|
// First guy in is the most specific so far.
|
||||||
|
mostSpecificMembers.add(member);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
boolean moreSpecific = true;
|
||||||
|
boolean lessSpecific = false;
|
||||||
|
|
||||||
|
// Is member more specific than everyone in the
|
||||||
|
// most-specific set?
|
||||||
|
for (Iterator specificIt = mostSpecificMembers.iterator();
|
||||||
|
specificIt.hasNext();) {
|
||||||
|
Member moreSpecificMember = (Member) specificIt.next();
|
||||||
|
|
||||||
|
if (! memberIsMoreSpecific(member, moreSpecificMember)) {
|
||||||
|
// if the candidate member is not more specific
|
||||||
|
// than this member, then it's not more specific
|
||||||
|
// than the entire set, but it may still be
|
||||||
|
// equivalently specific, so we check that next
|
||||||
|
moreSpecific = false;
|
||||||
|
|
||||||
|
// we check for a member of equal specificity by
|
||||||
|
// checking to see if this most specific member is
|
||||||
|
// explicitly more specific than the candidate
|
||||||
|
// member. if it is more specific, the candidate
|
||||||
|
// member can be chucked, otherwise we need to add
|
||||||
|
// the candidate member to the most-specific set
|
||||||
|
lessSpecific =
|
||||||
|
memberIsMoreSpecific(moreSpecificMember, member);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moreSpecific) {
|
||||||
|
// Member is the most specific now.
|
||||||
|
mostSpecificMembers.clear();
|
||||||
|
mostSpecificMembers.add(member);
|
||||||
|
|
||||||
|
} else if (! lessSpecific) {
|
||||||
|
// Add to ambiguity set if mutually unspecific.
|
||||||
|
mostSpecificMembers.add(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mostSpecificMembers.size() > 1) {
|
||||||
|
throw new NoSuchMethodException(
|
||||||
|
"Ambiguous request for member in "
|
||||||
|
+ clazz.getName()
|
||||||
|
+ " matching given args" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Member) mostSpecificMembers.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode ()
|
||||||
|
{
|
||||||
|
return clazz.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up the data structures for my target class's constructors.
|
||||||
|
*/
|
||||||
|
private void maybeLoadConstructors ()
|
||||||
|
{
|
||||||
|
if (ctorList == null) {
|
||||||
|
ctorList = new ArrayList();
|
||||||
|
Constructor[] ctors = clazz.getConstructors();
|
||||||
|
for (int i = 0; i < ctors.length; ++i) {
|
||||||
|
ctorList.add(ctors[i]);
|
||||||
|
paramMap.put(ctors[i], ctors[i].getParameterTypes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up the data structures for my target class's methods.
|
||||||
|
*/
|
||||||
|
private void maybeLoadMethods ()
|
||||||
|
{
|
||||||
|
if (methodMap == null) {
|
||||||
|
methodMap = new HashMap();
|
||||||
|
Method[] methods = clazz.getMethods();
|
||||||
|
|
||||||
|
for (int i = 0; i < methods.length; ++i) {
|
||||||
|
Method m = methods[i];
|
||||||
|
String methodName = m.getName();
|
||||||
|
Class[] paramTypes = m.getParameterTypes();
|
||||||
|
|
||||||
|
List list = (List) methodMap.get(methodName);
|
||||||
|
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList();
|
||||||
|
methodMap.put(methodName, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! ClassUtil.classIsAccessible(clazz)) {
|
||||||
|
m = ClassUtil.getAccessibleMethodFrom(
|
||||||
|
clazz, methodName, paramTypes );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m != null) {
|
||||||
|
list.add(m);
|
||||||
|
paramMap.put(m, paramTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param first a Member.
|
||||||
|
* @param second a Member.
|
||||||
|
*
|
||||||
|
* @return true if the first Member is more specific than the second,
|
||||||
|
* false otherwise. Specificity is determined according to the
|
||||||
|
* procedure in the Java Language Specification, section 15.12.2.
|
||||||
|
*/
|
||||||
|
private boolean memberIsMoreSpecific (Member first, Member second)
|
||||||
|
{
|
||||||
|
Class[] firstParamTypes = (Class[]) paramMap.get(first);
|
||||||
|
Class[] secondParamTypes = (Class[]) paramMap.get(second);
|
||||||
|
return ClassUtil.compatibleClasses(
|
||||||
|
secondParamTypes, firstParamTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The target class to look for methods and constructors in.
|
||||||
|
*/
|
||||||
|
private final Class clazz;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from method name to the Methods in the target class with
|
||||||
|
* that name.
|
||||||
|
*/
|
||||||
|
private final Map methodMap = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of the Constructors in the target class.
|
||||||
|
*/
|
||||||
|
private final List ctorList = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from a Constructor or Method object to the Class objects
|
||||||
|
* representing its formal parameters.
|
||||||
|
*/
|
||||||
|
private final Map paramMap = new HashMap();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user