blob: 71ee7cf2e5c24762d66fcc3ed1f5a4499d60ee66 [file] [log] [blame]
package org.eclipse.jem.internal.proxy.initParser;
/*******************************************************************************
* Copyright (c) 2001, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
/*
* $RCSfile: MethodHelper.java,v $
* $Revision: 1.2 $ $Date: 2004/02/03 23:18:36 $
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.lang.reflect.*;
/**
* This is a class to do message/constructor work.
* Specifically to find the most appropriate method.
*/
public class MethodHelper {
/*
* The class that is used to represent Null class type.
*
* @since 1.0.0
*/
private static class NULL_CLASS {
}
public static final Class NULL_TYPE = NULL_CLASS.class;
static final ArrayList sPrimitivesOrder;
static final int sCharPos;
static {
sPrimitivesOrder = new ArrayList(6);
sPrimitivesOrder.add(Byte.TYPE);
sPrimitivesOrder.add(Short.TYPE);
sPrimitivesOrder.add(Integer.TYPE);
sPrimitivesOrder.add(Long.TYPE);
sPrimitivesOrder.add(Float.TYPE);
sPrimitivesOrder.add(Double.TYPE);
// char can be treated like a short for purposes of ordering.
sCharPos = sPrimitivesOrder.indexOf(Short.TYPE);
}
/**
* Return whether the type2 can be assigned to type1 in
* method argument conversion.
*/
public static boolean isAssignableFrom(Class type1, Class type2) {
if (type1 == type2)
return true; // They're the same, so assignable.
if (type1.isPrimitive()) {
if (type2.isPrimitive()) {
if (type1 == Boolean.TYPE || type2 == Boolean.TYPE)
return false; // Since not equal and one is boolean and the other isn't, not assignable
int type1Pos = (type1 != Character.TYPE) ? sPrimitivesOrder.indexOf(type1) : sCharPos;
int type2Pos = (type2 != Character.TYPE) ? sPrimitivesOrder.indexOf(type2) : sCharPos;
return type1Pos > type2Pos; // It can be widened if type1 is higher in the order
}
return false; // primitive to non-primitive, not assignable.
} else
if (type2 == NULL_TYPE)
return true; // NULL_TYPE represents null for us, and null can be assigned to any object
else
return type1.isAssignableFrom(type2); // Can type2 be assigned to type1
}
/**
* Every entry in Array2 can be assigned to the corresponding entry in Array1.
*/
public static boolean isAssignableFrom(Class[] types1, Class[] types2) {
if (types1.length != types2.length)
return false; // Not the same size, so not compatible.
for (int i=0; i<types1.length; i++) {
if (!isAssignableFrom(types1[i], types2[i]))
return false;
}
return true; // All are assignable
}
/**
* Ambiguous Method Exception. I.E. There is more than one that could be used.
*/
public static class AmbiguousMethodException extends Exception {
public AmbiguousMethodException() {
}
public AmbiguousMethodException(String msg) {
super(msg);
}
}
/**
* Return the index of the most compatible method/constructor from the lists passed in.
* MethodsList: List of methods (if null then this is for constructors)
* ParmsList: List of parms for each method (each entry will be Class[]).
*/
private static int findMostCompatible(List methods, List parms) throws AmbiguousMethodException {
// The algorithm used is from the Java Language Specification 15.12.2.2
// First find the maximally specific ones
int maxSpecific = -1; // Index of the most specific one
Class[][] parmsCopy = (Class[][]) parms.toArray(new Class[parms.size()][]);
int size = parmsCopy.length;
// For each entry see if it is maximally specific
nextMethod: for (int i=0; i<size; i++) {
Class dclClassi = methods != null ? ((Method) methods.get(i)).getDeclaringClass() : null;
Class[] parmsi = parmsCopy[i];
for (int j=i+1; j<size; j++) {
if (parmsCopy[j] == null)
continue; // This one was thrown out
// Methodi is maximally specific if
// a) Methodi declaring class is assignable to Methodj declaring class
// b) Methodi parms are assignable to Methodj parms
//
// First see if Methodi is more specific, if it is
// then throw out Methodj and continue
// If Methodi is not compatible to Methodj, then test reverse comparison
boolean moreSpecificClass = true; // Default is more specific class
Class dclClassj = null;
if (dclClassi != null) {
// Step a
moreSpecificClass = isAssignableFrom(dclClassj = ((Method) methods.get(j)).getDeclaringClass(), dclClassi);
}
// Step b
Class[] parmsj = parmsCopy[j];
if (moreSpecificClass && isAssignableFrom(parmsj, parmsi)) {
// Methodi is more specific than Methodj, throw j out, continue to next j
parmsCopy[j] = null;
continue;
}
// Now see if Methodj is more specific than Methodi
moreSpecificClass = true;
if (dclClassi != null)
moreSpecificClass = isAssignableFrom(dclClassi, dclClassj);
if (moreSpecificClass && isAssignableFrom(parmsi, parmsj)) {
// Methodj is more specific than Methodi, go to the next i.
continue nextMethod;
}
// Neither method is more specific
}
// None more specific than Methodi, so add i to the maximally specific list.
if (maxSpecific != -1) {
// A quick test, if there is already a maximally specific one and the
// arglist differs, then it is immediately ambiguous. There can be
// more than one with the same parm list because interface methods will also
// show up in the methods list.
if (!Arrays.equals(parmsCopy[maxSpecific], parmsi))
throw new AmbiguousMethodException();
// A quick test, if i is not abstract, then save this one as the most specific
// because there will never be more than one non-abstract one of identical parm types.
// So the one already in the maxSpecific must be an abstract.
// If i is abstract, then don't replace the current most specific.
// If this is constructors, we shouldn't be here because you can't have more
// than one identical constructor
if (!Modifier.isAbstract(((Method) methods.get(i)).getModifiers()))
maxSpecific = i; // Replace the max specific with this one
} else
maxSpecific = i; // The first one
}
// maxSpecific has the choosen one, either the first of all the abstract ones, or the only
// non-abstract one.
return maxSpecific;
}
/**
* Find the most compatible method for the given arguments.
*/
public static Method findCompatibleMethod(Class receiver, String methodName, Class[] arguments) throws EvaluationException {
try {
Method mthd = receiver.getMethod(methodName, arguments);
return mthd; // Found exact match
} catch (NoSuchMethodException exc) {
// Need to find most compatible one.
Method mthds[] = receiver.getMethods();
ArrayList parmsList = new ArrayList(mthds.length); // The parm list from each compatible method.
ArrayList mthdsList = new ArrayList(mthds.length); // The list of compatible methods, same order as the parms above.
for (int i = 0; i<mthds.length; i++) {
Method mthd = mthds[i];
if (!mthd.getName().equals(methodName))
continue; // Not compatible, not same name
Class[] parms = mthd.getParameterTypes();
if (!isAssignableFrom(parms, arguments))
continue; // Not compatible, parms
// It is compatible with the requested method
parmsList.add(parms);
mthdsList.add(mthd);
}
// Now have list of compatible methods.
if (parmsList.size() == 0)
throw new EvaluationException(exc); // None found, so rethrow the exception
if (parmsList.size() == 1)
return (Method) mthdsList.get(0); // Only one, so return it
// Now find the most compatible method
try {
int mostCompatible = findMostCompatible(mthdsList, parmsList);
return (Method) mthdsList.get(mostCompatible);
} catch (AmbiguousMethodException e) {
throw new EvaluationException(new AmbiguousMethodException(methodName)); // Put the method name into the message
}
}
}
/**
* Find the most compatible constructor for the given arguments.
*/
public static java.lang.reflect.Constructor findCompatibleConstructor(Class receiver, Class[] arguments) throws EvaluationException {
try {
java.lang.reflect.Constructor ctor = receiver.getDeclaredConstructor(arguments);
ctor.setAccessible(true); // We allow all access, let ide and compiler handle security.
return ctor; // Found exact match
} catch (NoSuchMethodException exc) {
// Need to find most compatible one.
java.lang.reflect.Constructor ctors[] = receiver.getDeclaredConstructors();
ArrayList parmsList = new ArrayList(ctors.length); // The parm list from each compatible method.
ArrayList ctorsList = new ArrayList(ctors.length); // The list of compatible methods, same order as the parms above.
for (int i = 0; i<ctors.length; i++) {
java.lang.reflect.Constructor ctor = ctors[i];
Class[] parms = ctor.getParameterTypes();
if (!isAssignableFrom(parms, arguments))
continue; // Not compatible, parms
// It is compatible with the requested method
parmsList.add(parms);
ctorsList.add(ctor);
}
// Now have list of compatible methods.
if (parmsList.size() == 0)
throw new EvaluationException(exc); // None found, so rethrow the exception
if (parmsList.size() == 1) {
java.lang.reflect.Constructor ctor = (java.lang.reflect.Constructor) ctorsList.get(0); // Only one, so return it
ctor.setAccessible(true); // We allow all access, let ide and compilor handle security.
return ctor;
}
// Now find the most compatible ctor
try {
int mostCompatible = findMostCompatible(null, parmsList);
java.lang.reflect.Constructor ctor = (java.lang.reflect.Constructor) ctorsList.get(mostCompatible);
ctor.setAccessible(true); // We allow all access, let ide and compilor handle security.
return ctor;
} catch (AmbiguousMethodException e) {
throw new EvaluationException(new AmbiguousMethodException(receiver.getName())); // Put the class name into the message
}
}
}
}