package com.tns;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
class MethodResolver {
private static Map<String, String> primitiveTypesSignature = new HashMap<String, String>();
static {
// Boolean
primitiveTypesSignature.put("boolean", "Z");
primitiveTypesSignature.put("Boolean", "Z");
// Integer
primitiveTypesSignature.put("int", "I");
primitiveTypesSignature.put("Integer", "I");
// Double
primitiveTypesSignature.put("double", "D");
primitiveTypesSignature.put("Double", "D");
// Float
primitiveTypesSignature.put("float", "F");
primitiveTypesSignature.put("Float", "F");
// Short
primitiveTypesSignature.put("short", "S");
primitiveTypesSignature.put("Short", "S");
// Long
primitiveTypesSignature.put("long", "J");
primitiveTypesSignature.put("Long", "J");
// Char
primitiveTypesSignature.put("char", "C");
primitiveTypesSignature.put("Character", "C");
// Byte
primitiveTypesSignature.put("byte", "B");
primitiveTypesSignature.put("Byte", "B");
// Void
primitiveTypesSignature.put("void", "V");
}
private static class Tuple<X, Y> {
public final X x;
public final Y y;
public Tuple(X x, Y y) {
this.x = x;
this.y = y;
}
}
private static class DistanceComparator implements Comparator<Tuple<?, Integer>> {
@Override
public int compare(Tuple<?, Integer> left, Tuple<?, Integer> right) {
return left.y.compareTo(right.y);
}
}
private static DistanceComparator distanceComparator = new DistanceComparator();
private static Map<Constructor<?>, Class<?>[]> constructorParamTypeCache = new HashMap<Constructor<?>, Class<?>[]>();
public static String getMethodSignature(Class<?> retType, Class<?>[] params) {
StringBuilder ret = new StringBuilder();
ret.append('(');
for (int i = 0; i < params.length; i++) {
Class<?> type = params[i];
ret.append(getTypeSignature(type));
}
ret.append(')');
ret.append(getTypeSignature(retType));
return ret.toString();
}
public static String getTypeSignature(Class<?> type) {
if (type == null) {
return "V";
}
Class<?> t = type;
String array = "";
while (t.isArray()) {
array += "[";
t = t.getComponentType();
}
String signature = primitiveTypesSignature.get(t.getName());
if (signature == null) {
signature = "L" + t.getName().replace('.', '/') + ";";
}
return array + signature;
}
static HashMap<Class<?>, MethodFinder> methodOverloadsForClass = new HashMap<Class<?>, MethodFinder>();
static ArrayList<Tuple<Method, Integer>> candidates = new ArrayList<Tuple<Method, Integer>>();
static String resolveMethodOverload(Class<?> clazz, String methodName, Object[] args) throws ClassNotFoundException {
candidates.clear();
int argLength = (args != null) ? args.length : 0;
Class<?> c = clazz;
int iterationIndex = 0;
while (c != null) {
MethodFinder finder = methodOverloadsForClass.get(c);
if (finder == null) {
finder = new MethodFinder(c);
methodOverloadsForClass.put(c, finder);
}
ArrayList<Method> matchingMethods = finder.getMatchingMethods(methodName);
tryFindMatches(methodName, candidates, args, argLength, matchingMethods);
if (candidates.size() > iterationIndex && candidates.get(iterationIndex).y == 0) {
// direct matching (distance 0) found
break;
}
c = c.getSuperclass();
iterationIndex++;
}
if (!candidates.isEmpty()) {
if (candidates.size() > 1) {
Collections.sort(candidates, distanceComparator);
}
Method method = candidates.get(0).x;
return getMethodSignature(method.getReturnType(), method.getParameterTypes());
}
return null;
}
static void tryFindMatches(String methodName, ArrayList<Tuple<Method, Integer>> candidates, Object[] args, int argLength, ArrayList<Method> methods) {
for (Method method : methods) {
Class<?>[] params = method.getParameterTypes();
boolean success = false;
if (params.length == argLength) {
int dist = 0;
if (argLength == 0) {
success = true;
} else {
for (int i = 0; i < params.length; i++) {
if (args[i] != null) {
Class<?> argClass = args[i] instanceof NullObject ?
((NullObject)args[i]).getNullObjectClass()
: args[i].getClass();
Tuple<Boolean, Integer> res = isAssignableFrom(params[i], argClass);
success = res.x.booleanValue();
dist += res.y;
} else {
success = !params[i].isPrimitive();
}
if (!success) {
break;
}
}
}
if (success) {
candidates.add(new Tuple<Method, Integer>(method, Integer.valueOf(dist)));
if (dist == 0) {
break;
}
}
}
}
}
static String resolveConstructorSignature(Class<?> clazz, Object[] args) throws ClassNotFoundException, IOException {
Constructor<?> ctor = resolveConstructor(clazz, args);
return ctor != null ? getMethodSignature(null, ctor.getParameterTypes()) : null;
}
static Constructor<?> resolveConstructor(Class<?> clazz, Object[] args) throws ClassNotFoundException, IOException {
Constructor<?>[] constructors = clazz.getConstructors();
int argLen = (args != null) ? args.length : 0;
if (constructors.length == 1) {
if (argLen != constructors[0].getParameterTypes().length) {
return null;
}
return constructors[0];
}
ArrayList<Tuple<Constructor<?>, Integer>> candidates = new ArrayList<Tuple<Constructor<?>, Integer>>();
for (Constructor<?> constructor : constructors) {
int dist = 0;
Class<?>[] paramTypes = constructor.getParameterTypes();
boolean success = true;
if (args == null) {
if (paramTypes.length == 0) {
return constructor;
}
success = false;
} else {
if (paramTypes.length != argLen) {
continue;
}
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
Class<?> argClass = args[i] instanceof NullObject ?
((NullObject)args[i]).getNullObjectClass()
: args[i].getClass();
Tuple<Boolean, Integer> res = isAssignableFrom(paramTypes[i], argClass);
success = res.x.booleanValue();
dist += res.y;
} else {
success = !paramTypes[i].isPrimitive();
}
if (!success) {
break;
}
}
}
if (success) {
if (dist == 0) {
return constructor;
}
candidates.add(new Tuple<Constructor<?>, Integer>(constructor, Integer.valueOf(dist)));
}
}
if (!candidates.isEmpty()) {
Collections.sort(candidates, distanceComparator);
Constructor<?> selectedCtor = candidates.get(0).x;
return selectedCtor;
}
return null;
}
private static Tuple<Boolean, Integer> isAssignableFrom(Class<?> assignTo, Class<?> assignFrom) {
boolean success = false;
int dist = 0;
if (assignTo.isPrimitive()) {
if (assignTo.equals(byte.class)) {
if ((success = assignFrom.equals(Byte.class))) {
dist += 0;
} else if ((success = assignFrom.equals(Short.class))) {
dist += 1001;
} else if ((success = assignFrom.equals(Integer.class))) {
dist += 1002;
} else if ((success = assignFrom.equals(Long.class))) {
dist += 1003;
} else if ((success = assignFrom.equals(Float.class))) {
dist += 1004;
} else if ((success = assignFrom.equals(Double.class))) {
dist += 1005;
}
} else if (assignTo.equals(short.class)) {
if ((success = assignFrom.equals(Short.class))) {
dist += 0;
} else if ((success = assignFrom.equals(Byte.class))) {
dist += 1;
} else if ((success = assignFrom.equals(Integer.class))) {
dist += 2;
} else if ((success = assignFrom.equals(Long.class))) {
dist += 3;
} else if ((success = assignFrom.equals(Float.class))) {
dist += 4;
} else if ((success = assignFrom.equals(Double.class))) {
dist += 5;
}
} else if (assignTo.equals(int.class)) {
if ((success = assignFrom.equals(Integer.class))) {
dist += 0;
} else if ((success = assignFrom.equals(Short.class))) {
dist += 1;
} else if ((success = assignFrom.equals(Byte.class))) {
dist += 2;
} else if ((success = assignFrom.equals(Long.class))) {
dist += 3;
} else if ((success = assignFrom.equals(Float.class))) {
dist += 4;
} else if ((success = assignFrom.equals(Double.class))) {
dist += 5;
}
} else if (assignTo.equals(long.class)) {
if ((success = assignFrom.equals(Long.class))) {
dist += 0;
} else if ((success = assignFrom.equals(Integer.class))) {
dist += 1;
} else if ((success = assignFrom.equals(Short.class))) {
dist += 2;
} else if ((success = assignFrom.equals(Byte.class))) {
dist += 3;
} else if ((success = assignFrom.equals(Float.class))) {
dist += 4;
} else if ((success = assignFrom.equals(Double.class))) {
dist += 5;
}
} else if (assignTo.equals(float.class)) {
if ((success = assignFrom.equals(Float.class))) {
dist += 0;
} else if ((success = assignFrom.equals(Long.class))) {
dist += 1;
} else if ((success = assignFrom.equals(Integer.class))) {
dist += 2;
} else if ((success = assignFrom.equals(Short.class))) {
dist += 3;
} else if ((success = assignFrom.equals(Byte.class))) {
dist += 4;
} else if ((success = assignFrom.equals(Double.class))) {
dist += 5;
}
} else if (assignTo.equals(double.class)) {
if ((success = assignFrom.equals(Double.class))) {
dist += 0;
} else if ((success = assignFrom.equals(Float.class))) {
dist += 1;
} else if ((success = assignFrom.equals(Long.class))) {
dist += 2;
} else if ((success = assignFrom.equals(Integer.class))) {
dist += 3;
} else if ((success = assignFrom.equals(Short.class))) {
dist += 4;
} else if ((success = assignFrom.equals(Byte.class))) {
dist += 5;
}
} else if (assignTo.equals(boolean.class)) {
success = assignFrom.equals(Boolean.class);
} else if (assignTo.equals(char.class)) {
success = assignFrom.equals(Character.class);
}
} else {
success = assignTo.isAssignableFrom(assignFrom);
if (success) {
// TODO: consider interfaces as well
Class<?> currClass = assignFrom;
while (!assignTo.equals(currClass) && (currClass != null)) {
dist += 10 * 1000;
currClass = currClass.getSuperclass();
}
}
}
Tuple<Boolean, Integer> ret = new Tuple<Boolean, Integer>(Boolean.valueOf(success), Integer.valueOf(dist));
return ret;
}
public static boolean convertConstructorArgs(Constructor<?> ctor, Object[] args) {
boolean success = true;
if (ctor == null) {
success = false;
return success;
}
Class<?>[] paramTypes;
if (constructorParamTypeCache.containsKey(ctor)) {
paramTypes = constructorParamTypeCache.get(ctor);
} else {
paramTypes = ctor.getParameterTypes();
constructorParamTypeCache.put(ctor, paramTypes);
}
for (int i = 0; i < paramTypes.length; i++) {
Class<?> currParamType = paramTypes[i];
if (currParamType.isPrimitive()) {
success = convertPrimitiveArg(currParamType, args, i);
}
if (!success) {
break;
}
}
return success;
}
private static boolean convertPrimitiveArg(Class<?> primitiveType, Object[] args, int argIndex) {
boolean success = false;
Object currentArg = args[argIndex];
Class<?> currentArgClass = currentArg.getClass();
Number n;
if (primitiveType.equals(byte.class)) {
if (currentArgClass.equals(Byte.class)
|| currentArgClass.equals(Short.class)
|| currentArgClass.equals(Integer.class)
|| currentArgClass.equals(Long.class)
|| currentArgClass.equals(Float.class)
|| currentArgClass.equals(Double.class)) {
n = (Number) currentArg;
args[argIndex] = Byte.valueOf(n.byteValue());
success = true;
}
} else if (primitiveType.equals(short.class)) {
if (currentArgClass.equals(Byte.class)
|| currentArgClass.equals(Short.class)
|| currentArgClass.equals(Integer.class)
|| currentArgClass.equals(Long.class)
|| currentArgClass.equals(Float.class)
|| currentArgClass.equals(Double.class)) {
n = (Number) currentArg;
args[argIndex] = Short.valueOf(n.shortValue());
success = true;
}
} else if (primitiveType.equals(int.class)) {
if (currentArgClass.equals(Byte.class)
|| currentArgClass.equals(Short.class)
|| currentArgClass.equals(Integer.class)
|| currentArgClass.equals(Long.class)
|| currentArgClass.equals(Float.class)
|| currentArgClass.equals(Double.class)) {
n = (Number) currentArg;
args[argIndex] = Integer.valueOf(n.intValue());
success = true;
}
} else if (primitiveType.equals(long.class)) {
if (currentArgClass.equals(Byte.class)
|| currentArgClass.equals(Short.class)
|| currentArgClass.equals(Integer.class)
|| currentArgClass.equals(Long.class)
|| currentArgClass.equals(Float.class)
|| currentArgClass.equals(Double.class)) {
n = (Number) currentArg;
args[argIndex] = Long.valueOf(n.longValue());
success = true;
}
} else if (primitiveType.equals(float.class)) {
if (currentArgClass.equals(Byte.class)
|| currentArgClass.equals(Short.class)
|| currentArgClass.equals(Integer.class)
|| currentArgClass.equals(Long.class)
|| currentArgClass.equals(Float.class)
|| currentArgClass.equals(Double.class)) {
n = (Number) currentArg;
args[argIndex] = Float.valueOf(n.floatValue());
success = true;
}
} else if (primitiveType.equals(double.class)) {
if (currentArgClass.equals(Byte.class)
|| currentArgClass.equals(Short.class)
|| currentArgClass.equals(Integer.class)
|| currentArgClass.equals(Long.class)
|| currentArgClass.equals(Float.class)
|| currentArgClass.equals(Double.class)) {
n = (Number) currentArg;
args[argIndex] = Double.valueOf(n.doubleValue());
success = true;
}
} else if (primitiveType.equals(char.class)) {
success = currentArgClass.equals(Character.class);
} else if (primitiveType.equals(boolean.class)) {
success = currentArgClass.equals(Boolean.class);
}
return success;
}
static class MethodFinder {
private Method[] declaredMethods;
private HashMap<String, ArrayList<Method>> matchingMethods = new HashMap<String, ArrayList<Method>>();
public MethodFinder(Class<?> clazz) {
this.declaredMethods = clazz.getDeclaredMethods();
}
public ArrayList<Method> getMatchingMethods(String methodName) {
ArrayList<Method> matches = this.matchingMethods.get(methodName);
if (matches == null) {
matches = new ArrayList<Method>();
for (Method method : this.declaredMethods) {
if (!method.getName().equals(methodName)) {
continue;
}
int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) && !Modifier.isProtected(modifiers)) {
continue;
}
matches.add(method);
}
matchingMethods.put(methodName, matches);
}
return matches;
}
}
}