![JAR search and dependency download from the Maven repository](/logo.png)
org.cthul.objects.reflection.Signatures Maven / Gradle / Ivy
The newest version!
package org.cthul.objects.reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.cthul.objects.Boxing;
/**
*
*/
public class Signatures {
/**
* Finds the best constructor for the given arguments.
* @param
* @param clazz
* @param args
* @return constructor, or {@code null}
* @throws AmbiguousSignatureMatchException if multiple constructors match equally
*/
public static Constructor bestConstructor(Class clazz, Object[] args) throws AmbiguousConstructorMatchException {
return bestConstructor(collectConstructors(clazz), args);
}
/**
* Finds the best constructor for the given arguments types.
* @param
* @param clazz
* @param argTypes
* @return constructor, or {@code null}
* @throws AmbiguousSignatureMatchException if multiple constructors match equally
*/
public static Constructor bestConstructor(Class clazz, Class>[] argTypes) throws AmbiguousConstructorMatchException {
return bestConstructor(collectConstructors(clazz), argTypes);
}
/**
* Selects the best constructor for the given arguments.
* @param
* @param constructors
* @param args
* @return constructor, or {@code null}
* @throws AmbiguousSignatureMatchException if multiple constructors match equally
*/
public static Constructor bestConstructor(Constructor[] constructors, Object[] args) throws AmbiguousConstructorMatchException {
return bestConstructor(constructors, collectArgTypes(args));
}
/**
* Selects the best constructor for the given argument types.
* @param
* @param constructors
* @param argTypes
* @return constructor, or {@code null}
* @throws AmbiguousSignatureMatchException if multiple constructors match equally
*/
public static Constructor bestConstructor(Constructor[] constructors, Class>[] argTypes) throws AmbiguousConstructorMatchException {
try {
return best(constructors, collectSignatures(constructors), collectVarArgs(constructors), argTypes);
} catch (AmbiguousSignatureMatchException e) {
throw new AmbiguousConstructorMatchException(e, constructors);
}
}
/**
* Finds the best equally-matching constructors for the given arguments.
* @param
* @param clazz
* @param args
* @return constructors
*/
public static Constructor[] candidateConstructors(Class clazz, Object[] args) {
return candidateConstructors(collectConstructors(clazz), args);
}
/**
* Finds the best equally-matching constructors for the given argument types.
* @param
* @param clazz
* @param argTypes
* @return constructors
*/
public static Constructor[] candidateConstructors(Class clazz, Class>[] argTypes) {
return candidateConstructors(collectConstructors(clazz), argTypes);
}
/**
* Selects the best equally-matching constructors for the given arguments.
* @param
* @param constructors
* @param args
* @return constructors
*/
public static Constructor[] candidateConstructors(Constructor[] constructors, Object[] args) {
return candidateConstructors(constructors, collectArgTypes(args));
}
/**
* Selects the best equally-matching constructors for the given argument types.
* @param
* @param constructors
* @param argTypes
* @return constructors
*/
public static Constructor[] candidateConstructors(Constructor[] constructors, Class>[] argTypes) {
return candidates(constructors, collectSignatures(constructors), collectVarArgs(constructors), argTypes);
}
/**
* Finds the best method for the given arguments.
* @param clazz
* @param name
* @param args
* @return method
* @throws AmbiguousSignatureMatchException if multiple methods match equally
*/
public static Method bestMethod(Class> clazz, String name, Object[] args) throws AmbiguousMethodMatchException {
return bestMethod(collectMethods(clazz, name), args);
}
/**
* Finds the best method for the given argument types.
* @param clazz
* @param name
* @param argTypes
* @return method
* @throws AmbiguousSignatureMatchException if multiple methods match equally
*/
public static Method bestMethod(Class> clazz, String name, Class>[] argTypes) throws AmbiguousMethodMatchException {
return bestMethod(collectMethods(clazz, name), argTypes);
}
/**
* Selects the best method for the given arguments.
* @param methods
* @param args
* @return method
* @throws AmbiguousSignatureMatchException if multiple methods match equally
*/
public static Method bestMethod(Method[] methods, Object[] args) throws AmbiguousMethodMatchException {
return bestMethod(methods, collectArgTypes(args));
}
/**
* Selects the best method for the given argument types.
* @param methods
* @param argTypes
* @return method
* @throws AmbiguousSignatureMatchException if multiple methods match equally
*/
public static Method bestMethod(Method[] methods, Class>[] argTypes) throws AmbiguousMethodMatchException {
try {
return best(methods, collectSignatures(methods), collectVarArgs(methods), argTypes);
} catch (AmbiguousSignatureMatchException e) {
throw new AmbiguousMethodMatchException(e, methods);
}
}
/**
* Finds the best equally-matching methods for the given arguments.
* @param clazz
* @param name
* @param args
* @return methods
*/
public static Method[] candidateMethods(Class> clazz, String name, Object[] args) {
return candidateMethods(collectMethods(clazz, name), args);
}
/**
* Finds the best equally-matching methods for the given argument types.
* @param clazz
* @param name
* @param argTypes
* @return methods
*/
public static Method[] candidateMethods(Class> clazz, String name, Class>[] argTypes) {
return candidateMethods(collectMethods(clazz, name), argTypes);
}
/**
* Selects the best equally-matching methods for the given arguments.
* @param methods
* @param args
* @return methods
*/
public static Method[] candidateMethods(Method[] methods, Object[] args) {
return candidateMethods(methods, collectArgTypes(args));
}
/**
* Selects the best equally-matching methods for the given argument types.
* @param methods
* @param argTypes
* @return methods
*/
public static Method[] candidateMethods(Method[] methods, Class>[] argTypes) {
return candidates(methods, collectSignatures(methods), collectVarArgs(methods), argTypes);
}
/**
* Selects the best item for the given argument types.
* @param
* @param items
* @param signatures
* @param varArgs
* @param argTypes
* @return item
* @throws AmbiguousSignatureMatchException if multiple methods match equally
*/
public static T best(T[] items, Class>[][] signatures, boolean[] varArgs, Class>[] argTypes) throws AmbiguousSignatureMatchException {
int i = bestMatch(signatures, varArgs, argTypes);
if (i < 0) return null;
return items[i];
}
/**
* Selects the best equally-matching item for the given argument types.
* @param
* @param items
* @param signatures
* @param varArgs
* @param argTypes
* @return item
*/
public static T[] candidates(T[] items, Class>[][] signatures, boolean[] varArgs, Class>[] argTypes) {
final int[] indices = candidateMatches(signatures, varArgs, argTypes);
T[] result = newArray(items.getClass().getComponentType(), indices.length);
for (int i = 0; i < indices.length; i++) {
result[i] = items[indices[i]];
}
return result;
}
@SuppressWarnings("unchecked")
private static T[] newArray(Class> componentType, int length) {
return (T[]) Array.newInstance(componentType, length);
}
public static Class>[] collectArgTypes(Collection> args) {
final Class>[] result = new Class>[args.size()];
int i = 0;
for (Object a: args) {
result[i++] = argType(a);
}
return result;
}
/**
* Returnes the types for all values.
* For {@code null}-values, the type is {@code null}.
* @param args
* @return types
*/
public static Class>[] collectArgTypes(final Object[] args) {
final Class>[] result = new Class>[args.length];
collectArgTypes(args, result, 0, 0, args.length);
return result;
}
public static void collectArgTypes(final Object[] args, final Class>[] types, int argIndex, int typeIndex, int length) {
for (int i = 0; i < length; i++) {
Object a = args[i + argIndex];
types[i + typeIndex] = argType(a);
}
}
private static Class> argType(Object a) {
return a != null ? a.getClass() : null;
}
/**
* Collects all public methods of {@code clazz} with the given {@code name}.
* @param clazz
* @param name
* @return methods
*/
public static Method[] collectMethods(Class> clazz, String name) {
final List result = new ArrayList<>();
for (Method m: clazz.getMethods()) {
if (m.getName().equals(name)) result.add(m);
}
return result.toArray(new Method[result.size()]);
}
/**
* Collects methods of {@code clazz} with the given {@code name}.
* Methods are included if their modifier bits match each bit of {@code include}
* and no bit of {@code exclude}.
* @param clazz
* @param name
* @param include
* @param exclude
* @return methods
*/
public static Method[] collectMethods(Class> clazz, String name, int include, int exclude) {
final List result = new ArrayList<>();
collectMethods(result, new ArrayList[]>(), new HashSet>(), clazz, name, include, exclude);
return result.toArray(new Method[result.size()]);
}
private static void collectMethods(List methods, List[]> signatures, Set> visited, Class> clazz, String name, int include, int exclude) {
if (clazz == null || !visited.add(clazz)) return;
for (Method m: clazz.getDeclaredMethods()) {
String n = m.getName();
int mod = m.getModifiers();
if (name.equals(n) && include(mod, include, exclude)) {
Class>[] sig = m.getParameterTypes();
int len = methods.size();
boolean isNew = true;
for (int i = 0; i < len; i++) {
if (n.equals(methods.get(i).getName()) &&
Arrays.equals(sig, signatures.get(i))) {
isNew = false;
break;
}
}
if (isNew) {
methods.add(m);
signatures.add(sig);
}
}
}
collectMethods(methods, signatures, visited, clazz.getSuperclass(), name, include, exclude);
for (Class> i: clazz.getInterfaces()) {
collectMethods(methods, signatures, visited, i, name, include, exclude);
}
}
/**
* Collects all public constructors of {@code clazz}.
* @param
* @param clazz
* @return constructors
*/
@SuppressWarnings("unchecked")
public static Constructor[] collectConstructors(Class clazz) {
return (Constructor[]) clazz.getConstructors();
}
/**
* Collects constructors of {@code clazz}.
* Constructors are included if their modifier bits match each bit of {@code include}
* and no bit of {@code exclude}.
* @param
* @param clazz
* @param include
* @param exclude
* @return constructors
*/
@SuppressWarnings({"unchecked"})
public static Constructor[] collectConstructors(Class clazz, int include, int exclude) {
final List> result = new ArrayList<>();
for (Constructor> c: clazz.getDeclaredConstructors()) {
int mod = c.getModifiers();
if (include(mod, include, exclude)) {
result.add(c);
}
}
return (Constructor[]) result.toArray(new Constructor>[result.size()]);
}
private static boolean include(int mod, int include, int exclude) {
if (include == ANY) {
return (mod & exclude) == 0;
} else {
return (mod & include) == include && (mod & exclude) == 0;
}
}
public static Class>[][] collectSignatures(final Method[] methods) {
final Class>[][] result = new Class>[methods.length][];
for (int i = 0; i < methods.length; i++) {
result[i] = methods[i].getParameterTypes();
}
return result;
}
public static boolean[] collectVarArgs(final Method[] methods) {
final boolean[] result = new boolean[methods.length];
for (int i = 0; i < methods.length; i++) {
result[i] = methods[i].isVarArgs();
}
return result;
}
public static Class>[][] collectSignatures(final Constructor>[] constructors) {
final Class>[][] result = new Class>[constructors.length][];
for (int i = 0; i < constructors.length; i++) {
result[i] = constructors[i].getParameterTypes();
}
return result;
}
public static boolean[] collectVarArgs(final Constructor>[] constructors) {
final boolean[] result = new boolean[constructors.length];
for (int i = 0; i < constructors.length; i++) {
result[i] = constructors[i].isVarArgs();
}
return result;
}
/**
* Selects the best match in signatures for the given argument types.
* @param signatures
* @param varArgs
* @param argTypes
* @return index of best signature, -1 if nothing matched
* @throws AmbiguousSignatureMatchException if two signatures matched equally
*/
public static int bestMatch(Class>[][] signatures, boolean[] varArgs, Class>[] argTypes) throws AmbiguousSignatureMatchException {
return bestMatch(signatures, varArgs, new JavaSignatureComparator(argTypes));
}
/**
* @param signatures
* @param varArgs
* @param jsCmp
* @return index
* @see #bestMatch(java.lang.Class[][], boolean[], java.lang.Class[])
*/
public static int bestMatch(final Class>[][] signatures, final boolean[] varArgs, final JavaSignatureComparator jsCmp) throws AmbiguousSignatureMatchException {
int bestLevel = JavaSignatureComparator.NO_MATCH+1;
int bestIndex = -1;
LinkedList ambiguous = null;
for (int i = 0; i < signatures.length; i++) {
Class>[] sig = signatures[i];
boolean var = varArgs[i];
int level = jsCmp.applicability(sig, var);
if (level > bestLevel) {
// new is better
ambiguous = null;
bestLevel = level;
bestIndex = i;
} else if (level == bestLevel) {
// check against previous matches
if (ambiguous != null && !ambiguous.isEmpty()) {
boolean isAmbiguous = true;
Iterator it = ambiguous.iterator();
while (it.hasNext()) {
int a = it.next();
int c = jsCmp.compareSpecificness(signatures[a], varArgs[a], sig, var);
if (c < 0) isAmbiguous = false;
if (c > 0) it.remove();
}
if (isAmbiguous) {
if (ambiguous.isEmpty()) {
bestIndex = i;
} else {
ambiguous.add(i);
}
}
} else {
assert bestIndex > -1;
int c = jsCmp.compareSpecificness(signatures[bestIndex], varArgs[bestIndex], sig, var);
if (c > 0) {
bestIndex = i;
} else if (c == 0) {
if (ambiguous == null) ambiguous = new LinkedList<>();
ambiguous.add(bestIndex);
ambiguous.add(i);
}
}
}
}
if (ambiguous != null) {
if (ambiguous.size() > 1){
throw new AmbiguousSignatureMatchException(jsCmp, signatures, varArgs, Boxing.unboxIntegers(ambiguous));
} else if (ambiguous.size() == 1) {
return ambiguous.get(0);
}
}
return bestIndex;
}
/**
* Returns indices of all signatures that are a best match for the given
* argument types.
* @param signatures
* @param varArgs
* @param argTypes
* @return signature indices
*/
public static int[] candidateMatches(Class>[][] signatures, boolean[] varArgs, Class>[] argTypes) {
return candidateMatches(signatures, varArgs, new JavaSignatureComparator(argTypes));
}
/**
* @param signatures
* @param varArgs
* @param jsCmp
* @return signature indices
* @see #candidateMatches(java.lang.Class[][], boolean[], java.lang.Class[])
*/
public static int[] candidateMatches(Class>[][] signatures, boolean[] varArgs, JavaSignatureComparator jsCmp) {
int bestLevel = JavaSignatureComparator.NO_MATCH+1;
final LinkedList candidates = new LinkedList<>();
for (int i = 0; i < signatures.length; i++) {
Class>[] sig = signatures[i];
boolean var = varArgs[i];
int level = jsCmp.applicability(sig, var);
if (level > bestLevel) {
// new is better than all previous
candidates.clear();
bestLevel = level;
candidates.add(i);
} else if (level == bestLevel) {
// check against previous matches
boolean isCandidate = true;
Iterator it = candidates.iterator();
while (it.hasNext()) {
int k = it.next();
int c = jsCmp.compareSpecificness(signatures[k], varArgs[k], sig, var);
if (c < 0) isCandidate = false;
if (c > 0) it.remove();
}
if (isCandidate) {
candidates.add(i);
}
}
}
return Boxing.unboxIntegers(candidates);
}
/**
* Fixes an arguments array to fit parameter types,
* where the last parameter is an varargs array.
* @param paramsWithVarArgs
* @param arguments
* @return fixed arguments array
*/
public static Object[] fixVarArgs(Class>[] paramsWithVarArgs, Object[] arguments) {
int n = paramsWithVarArgs.length;
return fixVarArgs(n, paramsWithVarArgs[n-1], arguments);
}
/**
* Fixes an arguments array to fit a given length,
* the last value is an array filled with varargs.
* @param length
* @param varArgType
* @param arguments
* @return fixed arguments array
*/
@SuppressWarnings("SuspiciousSystemArraycopy")
public static Object[] fixVarArgs(int length, Class> varArgType, Object[] arguments) {
final Object[] result;
if (arguments.length == length && varArgType.isInstance(arguments[length-1])) {
return arguments;
} else {
result = Arrays.copyOf(arguments, length, Object[].class);
}
final Object varArgs;
Class> varArgElementType = varArgType.getComponentType();
if (varArgElementType.isPrimitive()) {
varArgs = Boxing.unboxAll(varArgElementType, arguments, length-1, -1);
} else {
int varLen = arguments.length - length + 1;
varArgs = Array.newInstance(varArgElementType, varLen);
System.arraycopy(arguments, length-1, varArgs, 0, varLen);
}
result[length-1] = varArgs;
return result;
}
/**
* Invokes {@code constructor}, automatically tries to fix varargs arguments.
* @param
* @param constructor
* @param args
* @return new instance
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
public static T newInstance(Constructor constructor, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
if (constructor.isVarArgs()) {
args = fixVarArgs(constructor.getParameterTypes(), args);
}
return constructor.newInstance(args);
}
/**
* Invokes {@code method}, automatically tries to fix varargs arguments.
* @param instance
* @param method
* @param args
* @return result
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static Object invoke(Object instance, Method method, Object... args) throws IllegalAccessException, InvocationTargetException {
if (method.isVarArgs()) {
args = fixVarArgs(method.getParameterTypes(), args);
}
return method.invoke(instance, args);
}
public static final int NONE = 0;
public static final int ANY = 0;
public static final int STATIC = Modifier.STATIC;
public static final int PRIVATE = Modifier.PRIVATE;
public static final int PROTECTED = Modifier.PROTECTED;
public static final int PUBLIC = Modifier.PUBLIC;
public static final int NOT_DEFAULT = PRIVATE | PROTECTED | PUBLIC;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy