All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brooklyn.util.javalang.Reflections Maven / Gradle / Ivy

Go to download

Utility classes and methods developed for Brooklyn but not dependendent on Brooklyn or much else

There is a newer version: 0.7.0-M1
Show newest version
package brooklyn.util.javalang;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import brooklyn.util.collections.MutableList;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

/**
 * Reflection utilities ("borrowed" from cloudsoft monterey).
 * 
 * @author aled
 */
public class Reflections {

    private static final Logger LOG = LoggerFactory.getLogger(Reflections.class);

	public static class ReflectionNotFoundException extends RuntimeException {
		private static final long serialVersionUID = 9032835250796708037L;
		public ReflectionNotFoundException(String message, Throwable cause) {
			super(message, cause);
		}
		public ReflectionNotFoundException(String message) {
			super(message);
		}
	}

	public static class ReflectionAccessException extends RuntimeException {
		private static final long serialVersionUID = 6569605861192432009L;

		public ReflectionAccessException(String message, Throwable cause) {
			super(message, cause);
		}
	}

	private final ClassLoader classLoader;
	
	public Reflections(ClassLoader classLoader) {
		this.classLoader = checkNotNull(classLoader);
	}

	public Object loadInstance(String classname, Object[] argValues) throws ReflectionNotFoundException, ReflectionAccessException {
        Class clazz = loadClass(classname);
        Optional v = null;
        try {
            v = invokeConstructorWithArgs(clazz, argValues);
            if (v.isPresent()) return v.get();
        } catch (Exception e) {
            throw new IllegalStateException("Error invoking constructor for "+clazz+Arrays.toString(argValues)+": "+e);
        }
        throw new IllegalStateException("No suitable constructor for "+clazz+Arrays.toString(argValues));
	}
	public Object loadInstance(String classname, Class[] argTypes, Object[] argValues) throws ReflectionNotFoundException, ReflectionAccessException {
		Class clazz = loadClass(classname);
		Constructor constructor = loadConstructor(clazz, argTypes);
		return loadInstance(constructor, argValues);
	}

	public Object loadInstance(String classname) throws ReflectionNotFoundException, ReflectionAccessException {
		Class clazz = loadClass(classname);
		try {
			return clazz.newInstance();
		} catch (InstantiationException e) {
			throw new ReflectionAccessException("Failed to create instance of class '" + classname + "' using class loader " + classLoader, e);
		} catch (IllegalAccessException e) {
			throw new ReflectionAccessException("Failed to create instance of class '" + classname + "' using class loader " + classLoader, e);
		}
	}

	/** instantiates the given class from its binary name */
	public Class loadClass(String classname) throws ReflectionNotFoundException {
		try {
			return classLoader.loadClass(classname);
		} catch (ClassNotFoundException e) {
			throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader, e);
		} catch (NoClassDefFoundError e) {
			throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader, e);
		} catch (UnsupportedClassVersionError e) {
			throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader, e);
		}
	}

	/** given a nested part, e.g. Inner$VeryInner, this will recurse through clazz.Inner, looking for VeryInner,
	 * then looking in each supertype (interface) of clazz for Inner.VeryInner;
	 * 

* so it will find Clazz.Inner.VeryInner wherever in the hierarchy it is defined *

* (as opposed to ClassLoader which requires Inner.VeryInner to be _declared_ in clazz, not in any supertype *

* returns null if not found */ public static Class loadInnerClassPossiblyInheritted(Class clazz, String nestedPart) throws ReflectionNotFoundException { Set visited = new HashSet(); Class result = loadInnerClassPossiblyInheritted(visited, clazz, nestedPart); if (result!=null) return result; throw new ReflectionNotFoundException("Inner class " + nestedPart + " could not be found in " + clazz + " or any of its super-types"); } /** as 2-arg, but maintains set of visited elements, and returns null if not found */ private static Class loadInnerClassPossiblyInheritted(Set visited, Class clazz, String nestedPart) throws ReflectionNotFoundException { if (clazz==null) return null; if (nestedPart==null || nestedPart.length()==0) return clazz; int i1 = nestedPart.indexOf('$'); int i2 = nestedPart.indexOf('.'); int idx = (i2 > -1 && (i2 < i1 || i1==-1) ? i2 : i1); String thisClassToFind = nestedPart; String nextClassesToFind = ""; if (idx>=0) { thisClassToFind = nestedPart.substring(0, idx); nextClassesToFind = nestedPart.substring(idx+1); } if (!visited.add(clazz.getCanonicalName()+"!"+nestedPart)) { //already visited return null; } Class[] members = clazz.getClasses(); for (int i = 0; i < members.length; i++) { if (members[i].getSimpleName().equals(thisClassToFind)) { Class clazzI = loadInnerClassPossiblyInheritted(visited, members[i], nextClassesToFind); if (clazzI!=null) return clazzI; } } //look in supertype first (not sure if necessary) Class result = loadInnerClassPossiblyInheritted(visited, clazz.getSuperclass(), nestedPart); if (result!=null) return result; for (Class iface : clazz.getInterfaces()) { result = loadInnerClassPossiblyInheritted(visited, iface, nestedPart); if (result!=null) return result; } return null; } /** does not look through ancestors of outer class */ public Class loadInnerClassNotInheritted(String outerClassname, String innerClassname) throws ReflectionNotFoundException { return loadClass(outerClassname + "$" + innerClassname); } /** does not look through ancestors of outer class *

* uses the classloader set in this class, not in the clazz supplied */ public Class loadInnerClassNotInheritted(Class outerClazz, String innerClassname) throws ReflectionNotFoundException { return loadClass(outerClazz.getName() + "$" + innerClassname); } public Constructor loadConstructor(Class clazz, Class[] argTypes) throws ReflectionAccessException { try { return clazz.getConstructor(argTypes); } catch (SecurityException e) { throw new ReflectionAccessException("Failed to load constructor of class '" + clazz + " with argument types " + Arrays.asList(argTypes), e); } catch (NoSuchMethodException e) { throw new ReflectionAccessException("Failed to load constructor of class '" + clazz + " with argument types " + Arrays.asList(argTypes), e); } } /** * Returns a constructor that accepts the given arguments, or null if no such constructor is * accessible. *

* This does not support varargs. * * @deprecated since 0.6.0 use {@link #invokeConstructorWithArgs(Class, Object[])} */ @Deprecated @SuppressWarnings("unchecked") public static Constructor findCallabaleConstructor(Class clazz, Object[] args) { for (Constructor constructor : clazz.getConstructors()) { if (typesMatch(args, constructor.getParameterTypes())) { return (Constructor) constructor; } } return null; } /** Invokes a suitable constructor, supporting varargs and primitives */ public static Optional invokeConstructorWithArgs(Class clazz, Object[] argsArray) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { return invokeConstructorWithArgs(clazz, argsArray, false); } /** Invokes a suitable constructor, supporting varargs and primitives, additionally supporting setAccessible */ @SuppressWarnings("unchecked") public static Optional invokeConstructorWithArgs(Class clazz, Object[] argsArray, boolean setAccessible) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { for (Constructor constructor : clazz.getConstructors()) { Class[] parameterTypes = constructor.getParameterTypes(); if (constructor.isVarArgs()) { if (typesMatchUpTo(argsArray, parameterTypes, parameterTypes.length-1)) { Class varargType = parameterTypes[parameterTypes.length-1].getComponentType(); boolean varargsMatch = true; for (int i=parameterTypes.length-1; i) Optional.of(constructor.newInstance(newArgsArray)); } } } if (typesMatch(argsArray, parameterTypes)) { if (setAccessible) constructor.setAccessible(true); return (Optional) Optional.of(constructor.newInstance(argsArray)); } } return Optional.absent(); } /** returns a single constructor in a given class, or throws an exception */ public Constructor loadSingleConstructor(Class clazz) { Constructor[] constructors = clazz.getConstructors(); if (constructors.length == 1) { return constructors[0]; } throw new IllegalArgumentException("Class " + clazz + " has more than one constructor"); } public T loadInstance(Constructor constructor, Object[] argValues) throws IllegalArgumentException, ReflectionAccessException { try { try { return constructor.newInstance(argValues); } catch (IllegalArgumentException e) { try { LOG.warn("Failure passing provided arguments ("+getIllegalArgumentsErrorMessage(constructor, argValues)+"; "+e+"); attempting to reconstitute"); argValues = (Object[]) updateFromNewClassLoader(argValues); return constructor.newInstance(argValues); } catch (Throwable e2) { LOG.warn("Reconstitution attempt failed (will rethrow original excaption): "+e2, e2); throw e; } } } catch (IllegalArgumentException e) { throw new IllegalArgumentException(getIllegalArgumentsErrorMessage(constructor, argValues), e); } catch (InstantiationException e) { throw new ReflectionAccessException("Failed to create instance of '" + constructor.getDeclaringClass(), e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to create instance of '" + constructor.getDeclaringClass(), e); } catch (InvocationTargetException e) { throw new ReflectionAccessException("Failed to create instance of '" + constructor.getDeclaringClass(), e); } } public Method loadMethod(Class clazz, String methodName, Class[] argTypes) throws ReflectionNotFoundException, ReflectionAccessException { try { return clazz.getMethod(methodName, argTypes); } catch (NoClassDefFoundError e) { throw new ReflectionNotFoundException("Failed to invoke method '" + methodName + " on class " + clazz + " with argument types " + Arrays.asList(argTypes) + ", using class loader " + clazz.getClassLoader(), e); } catch (NoSuchMethodException e) { throw new ReflectionNotFoundException("Failed to invoke method '" + methodName + " on class " + clazz + " with argument types " + Arrays.asList(argTypes), e); } catch (SecurityException e) { throw new ReflectionAccessException("Failed to invoke method '" + methodName + " on class " + clazz + " with argument types " + Arrays.asList(argTypes), e); } } /** returns the first method matching the given name */ public Method loadMethod(Class clazz, String methodName) throws ReflectionNotFoundException, ReflectionAccessException { try { Method[] allmethods = clazz.getMethods(); for (int i = 0; i < allmethods.length; i++) { if (allmethods[i].getName().equals(methodName)) { return allmethods[i]; } } throw new ReflectionNotFoundException("Cannot find method " + methodName + " on class " + clazz); } catch (SecurityException e) { throw new ReflectionAccessException("Failed to invoke method '" + methodName + " on class " + clazz, e); } } /** * * @throws ReflectionAccessException If invocation failed due to illegal access or the invoked method failed * @throws IllegalArgumentException If the arguments were invalid */ public Object invokeMethod(Method method, Object obj, Object... argValues) throws ReflectionAccessException { try { return method.invoke(obj, argValues); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(getIllegalArgumentsErrorMessage(method, argValues), e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to invoke method '" + method.toGenericString() + " on class " + method.getDeclaringClass() + " with argument values " + Arrays.asList(argValues), e); } catch (InvocationTargetException e) { throw new ReflectionAccessException("Failed to invoke method '" + method.toGenericString() + " on class " + method.getDeclaringClass() + " with argument values " + Arrays.asList(argValues), e); } } public Object invokeStaticMethod(Method method, Object... argValues) throws IllegalArgumentException, ReflectionAccessException { try { return method.invoke(null, argValues); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(getIllegalArgumentsErrorMessage(method, argValues), e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to invoke method '" + method.toGenericString() + " on class " + method.getDeclaringClass() + " with argument values " + Arrays.asList(argValues), e); } catch (InvocationTargetException e) { throw new ReflectionAccessException("Failed to invoke method '" + method.toGenericString() + " on class " + method.getDeclaringClass() + " with argument values " + Arrays.asList(argValues), e); } } public Object loadStaticField(Class clazz, String fieldname) throws ReflectionAccessException { return loadStaticFields(clazz, new String[] {fieldname}, null)[0]; } public Object[] loadStaticFields(Class clazz, String[] fieldnamesArray, Object[] defaults) throws ReflectionAccessException { Object[] result = new Object[fieldnamesArray.length]; if (defaults!=null) { for (int i = 0; i < defaults.length; i++) { result[i] = defaults[i]; } } List fieldnames = Arrays.asList(fieldnamesArray); Field[] classFields = clazz.getDeclaredFields(); for (int i = 0; i < classFields.length; i++) { Field field = classFields[i]; int index = fieldnames.indexOf(field.getName()); if (index >= 0) { try { result[index] = field.get(null); } catch (IllegalArgumentException e) { throw new ReflectionAccessException("Failed to load field '" + field.getName() + " from class " + clazz, e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to load field '" + field.getName() + " from class " + clazz, e); } } } return result; } private static String getIllegalArgumentsErrorMessage(Method method, Object[] argValues) { return method.toGenericString() + " not applicable for the parameters of type " + argumentTypesToString(argValues); } private static String getIllegalArgumentsErrorMessage(Constructor constructor, Object[] argValues) { return constructor.toGenericString() + " not applicable for the parameters of type " + argumentTypesToString(argValues); } private static String argumentTypesToString(Object[] argValues) { StringBuffer msg = new StringBuffer("("); for (int i = 0; i < argValues.length; i++) { if (i != 0) msg.append(", "); msg.append(argValues[i] != null ? argValues[i].getClass().getName() : "null"); } msg.append(")"); return msg.toString(); } /** copies all fields from the source to target; very little compile-time safety checking, so use with care * @throws IllegalAccessException * @throws IllegalArgumentException */ public static void copyFields(T source, T target) throws IllegalArgumentException, IllegalAccessException { Class clazz = source.getClass(); while (clazz!=null) { Field[] fields = clazz.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); Object vs = f.get(source); Object vt = f.get(target); if ((vs==null && vt!=null) || (vs!=null && !vs.equals(vt))) { f.set(target, vs); } } clazz = clazz.getSuperclass(); } } /** * Loads class given its canonical name format (e.g. com.acme.Foo.Inner), * using iterative strategy (trying com.acme.Foo$Inner, then com.acme$Foo$Inner, etc). * @throws ReflectionNotFoundException */ public Class loadClassFromCanonicalName(String canonicalName) throws ClassNotFoundException, ReflectionNotFoundException { ClassNotFoundException err = null; String name = canonicalName; do { try { return classLoader.loadClass(name); } catch (ClassNotFoundException e) { if (err == null) err = e; int lastIndexOf = name.lastIndexOf("."); if (lastIndexOf >= 0) { name = name.substring(0, lastIndexOf) + "$" + name.substring(lastIndexOf+1); } } } while (name.contains(".")); throw err; } /** finds the resource in the classloader, if it exists; inserts or replaces leading slash as necessary * (i believe it should _not_ have one, but there is some inconsistency) * * Will return null if no resource is found. */ @Nullable public URL getResource(String r) { URL u = null; u = classLoader.getResource(r); if (u!=null) return u; if (r.startsWith("/")) r = r.substring(1); else r = "/"+r; return classLoader.getResource(r); } /** * Serialize the given object, then reload using the current class loader; * this removes linkages to instances with classes loaded by an older class loader. *

* (like a poor man's clone) *

* aka "reconstitute(Object)" */ public final Object updateFromNewClassLoader(Object data) throws IOException, ClassNotFoundException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); new ObjectOutputStream(bytes).writeObject(data); Object reconstituted = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())).readObject(); if (LOG.isDebugEnabled()) LOG.debug("Reconstituted data: " + reconstituted + ", class loader: " + classLoader); return reconstituted; } public ClassLoader getClassLoader() { return classLoader; } /** @deprecated since 0.6.0; this is sloppy, and rarely does the right thing. * fortunately don't think it is used. * use methods in {@link StackTraceSimplifier} or {@link JavaClassNames} */ @Deprecated public static StackTraceElement getCaller() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); // 0 is Thread.getStackTrace() // 1 is Reflections.getCaller() // 2 is our caller in pure java -- or a groovy/interceptor in some cases // 3 is our caller in groovy sometimes return stackTrace[3]; } @SuppressWarnings("unchecked") public static Class findSuperType(T impl, String typeName) { Set> toinspect = new LinkedHashSet>(); Set> inspected = new HashSet>(); toinspect.add(impl.getClass()); while (toinspect.size() > 0) { Class clazz = toinspect.iterator().next(); // get and remove the first element if (clazz.getName().equals(typeName)) { return (Class) clazz; } inspected.add(clazz); List> toAdd = Arrays.asList(clazz.getInterfaces()); toinspect.addAll( toAdd ); if (clazz.getSuperclass() != null) toinspect.add(clazz.getSuperclass()); toinspect.removeAll(inspected); } return null; } /** whereas Class.getInterfaces() only returns interfaces directly implemented by a class, * this walks the inheritance hierarchy to include interfaces implemented by superclass/ancestors; * (note it does not include superinterfaces) */ public static Set> getInterfacesIncludingClassAncestors(Class clazz) { Set> result = new LinkedHashSet>(); while (clazz!=null) { for (Class iface: clazz.getInterfaces()) result.add(iface); clazz = clazz.getSuperclass(); } return result; } public static Method findMethod(Class clazz, String name, Class... parameterTypes) throws NoSuchMethodException { if (clazz == null || name == null) { throw new NullPointerException("Must not be null: clazz="+clazz+"; name="+name); } Class clazzToInspect = clazz; NoSuchMethodException toThrowIfFails = null; while (clazzToInspect != null) { try { return clazzToInspect.getDeclaredMethod(name, parameterTypes); } catch (NoSuchMethodException e) { if (toThrowIfFails == null) toThrowIfFails = e; clazzToInspect = clazzToInspect.getSuperclass(); } } throw toThrowIfFails; } public static Field findField(Class clazz, String name) throws NoSuchFieldException { if (clazz == null || name == null) { throw new NullPointerException("Must not be null: clazz="+clazz+"; name="+name); } Class clazzToInspect = clazz; NoSuchFieldException toThrowIfFails = null; while (clazzToInspect != null) { try { return clazzToInspect.getDeclaredField(name); } catch (NoSuchFieldException e) { if (toThrowIfFails == null) toThrowIfFails = e; clazzToInspect = clazzToInspect.getSuperclass(); } } throw toThrowIfFails; } public static List findPublicFieldsOrderedBySuper(Class clazz) { checkNotNull(clazz, "clazz"); MutableList.Builder result = MutableList.builder(); Stack> tovisit = new Stack>(); Set> visited = Sets.newLinkedHashSet(); tovisit.push(clazz); while (!tovisit.isEmpty()) { Class nextclazz = tovisit.pop(); if (!visited.add(nextclazz)) { continue; // already visited } if (nextclazz.getSuperclass() != null) tovisit.add(nextclazz.getSuperclass()); tovisit.addAll(Arrays.asList(nextclazz.getInterfaces())); result.addAll(Iterables.filter(Arrays.asList(nextclazz.getDeclaredFields()), new Predicate() { @Override public boolean apply(Field input) { return Modifier.isPublic(input.getModifiers()); }})); } List resultList = result.build(); Collections.sort(resultList, new Comparator() { @Override public int compare(Field f1, Field f2) { Field fsubbest = inferSubbestField(f1, f2); return (fsubbest == null) ? 0 : (fsubbest == f1 ? 1 : -1); }}); return resultList; } // TODO I've seen strange behaviour where class.getMethods() does not include methods from interfaces. // Also the ordering guarantees here are useful... public static List findPublicMethodsOrderedBySuper(Class clazz) { checkNotNull(clazz, "clazz"); MutableList.Builder result = MutableList.builder(); Stack> tovisit = new Stack>(); Set> visited = Sets.newLinkedHashSet(); tovisit.push(clazz); while (!tovisit.isEmpty()) { Class nextclazz = tovisit.pop(); if (!visited.add(nextclazz)) { continue; // already visited } if (nextclazz.getSuperclass() != null) tovisit.add(nextclazz.getSuperclass()); tovisit.addAll(Arrays.asList(nextclazz.getInterfaces())); result.addAll(Iterables.filter(Arrays.asList(nextclazz.getDeclaredMethods()), new Predicate() { @Override public boolean apply(Method input) { return Modifier.isPublic(input.getModifiers()); }})); } List resultList = result.build(); Collections.sort(resultList, new Comparator() { @Override public int compare(Method m1, Method m2) { Method msubbest = inferSubbestMethod(m1, m2); return (msubbest == null) ? 0 : (msubbest == m1 ? 1 : -1); }}); return resultList; } /** * Gets the field that is in the sub-class; or null if one field does not come from a sub-class of the other field's class */ public static Field inferSubbestField(Field f1, Field f2) { Class c1 = f1.getDeclaringClass(); Class c2 = f2.getDeclaringClass(); boolean isSuper1 = c1.isAssignableFrom(c2); boolean isSuper2 = c2.isAssignableFrom(c1); return (isSuper1) ? (isSuper2 ? null : f2) : (isSuper2 ? f1 : null); } /** * Gets the method that is in the sub-class; or null if one method does not come from a sub-class of the other method's class */ public static Method inferSubbestMethod(Method m1, Method m2) { Class c1 = m1.getDeclaringClass(); Class c2 = m2.getDeclaringClass(); boolean isSuper1 = c1.isAssignableFrom(c2); boolean isSuper2 = c2.isAssignableFrom(c1); return (isSuper1) ? (isSuper2 ? null : m2) : (isSuper2 ? m1 : null); } /** * Gets the class that is in the sub-class; or null if neither is a sub-class of the other. */ public static Class inferSubbest(Class c1, Class c2) { boolean isSuper1 = c1.isAssignableFrom(c2); boolean isSuper2 = c2.isAssignableFrom(c1); return (isSuper1) ? (isSuper2 ? null : c2) : (isSuper2 ? c1 : null); } /** convenience for casting the given candidate to the given type (without any coercion, and allowing candidate to be null) */ @SuppressWarnings("unchecked") public static T cast(Object candidate, Class type) { if (candidate==null) return null; if (!type.isAssignableFrom(candidate.getClass())) throw new IllegalArgumentException("Requires a "+type+", but had a "+candidate.getClass()+" ("+candidate+")"); return (T)candidate; } /** invokes the given method on the given clazz or instance, doing reasonably good matching on args etc * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ public static Optional invokeMethodWithArgs(Object clazzOrInstance, String method, List args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { return invokeMethodWithArgs(clazzOrInstance, method, args, false); } public static Optional invokeMethodWithArgs(Object clazzOrInstance, String method, List args, boolean setAccessible) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Preconditions.checkNotNull(clazzOrInstance, "clazz or instance"); Preconditions.checkNotNull(method, "method"); Preconditions.checkNotNull(args, "args to "+method); Class clazz; Object instance; if (clazzOrInstance instanceof Class) { clazz = (Class)clazzOrInstance; instance = null; } else { clazz = clazzOrInstance.getClass(); instance = clazzOrInstance; } Object[] argsArray = args.toArray(); for (Method m: clazz.getMethods()) { if (method.equals(m.getName())) { Class[] parameterTypes = m.getParameterTypes(); if (m.isVarArgs()) { if (typesMatchUpTo(argsArray, parameterTypes, parameterTypes.length-1)) { Class varargType = parameterTypes[parameterTypes.length-1].getComponentType(); boolean varargsMatch = true; for (int i=parameterTypes.length-1; i[] parameterTypes) { if (argsArray.length != parameterTypes.length) return false; return typesMatchUpTo(argsArray, parameterTypes, argsArray.length); } /** true iff the initial N args match the corresponding types */ public static boolean typesMatchUpTo(Object[] argsArray, Class[] parameterTypes, int lengthRequired) { if (argsArray.length < lengthRequired || parameterTypes.length < lengthRequired) return false; for (int i=0; i