/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.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.Map; import java.util.Set; import java.util.Stack; import javax.annotation.Nullable; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.exceptions.Exceptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; 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; private final Map<String, String> classRenameMap = MutableMap.of(); public Reflections(ClassLoader classLoader) { this.classLoader = checkNotNull(classLoader); } /** supply a map of known renames, of the form "old-class -> new-class" */ public Reflections applyClassRenames(Map<String,String> newClassRenames) { this.classRenameMap.putAll(newClassRenames); return this; } 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) + ": " + Exceptions.collapseText(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 + ": " + Exceptions.collapseText(e), e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to create instance of class '" + classname + "' using class loader " + classLoader + ": " + Exceptions.collapseText(e), e); } } /** instantiates the given class from its binary name */ public Class<?> loadClass(String classname) throws ReflectionNotFoundException { try { classname = findMappedNameAndLog(classRenameMap, classname); return classLoader.loadClass(classname); } catch (ClassNotFoundException e) { throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader + ": " + Exceptions.collapseText(e), e); } catch (NoClassDefFoundError e) { throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader + ": " + Exceptions.collapseText(e), e); } catch (UnsupportedClassVersionError e) { throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader + ": " + Exceptions.collapseText(e), e); } } @SuppressWarnings("unchecked") public <T> Class<? extends T> loadClass(String classname, Class<T> superType) throws ReflectionNotFoundException { return (Class<? extends T>) loadClass(classname); } /** 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; * <p> * so it will find Clazz.Inner.VeryInner wherever in the hierarchy it is defined * <p> * (as opposed to ClassLoader which requires Inner.VeryInner to be _declared_ in clazz, not in any supertype * <p> * returns null if not found */ public static Class<?> loadInnerClassPossiblyInheritted(Class<?> clazz, String nestedPart) throws ReflectionNotFoundException { Set<String> visited = new HashSet<String>(); 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<String> 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 * <p> * 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) + ": " + Exceptions.collapseText(e), e); } catch (NoSuchMethodException e) { throw new ReflectionAccessException("Failed to load constructor of class '" + clazz + " with argument types " + Arrays.asList(argTypes) + ": " + Exceptions.collapseText(e), e); } } /** Invokes a suitable constructor, supporting varargs and primitives */ public static <T> Optional<T> invokeConstructorWithArgs(ClassLoader classLoader, String className, Object...argsArray) { Reflections reflections = new Reflections(classLoader); @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) reflections.loadClass(className); return invokeConstructorWithArgs(reflections, clazz, argsArray, false); } /** Invokes a suitable constructor, supporting varargs and primitives */ public static <T> Optional<T> invokeConstructorWithArgs(ClassLoader classLoader, Class<T> clazz, Object[] argsArray, boolean setAccessible) { Reflections reflections = new Reflections(classLoader); return invokeConstructorWithArgs(reflections, clazz, argsArray, setAccessible); } /** Invokes a suitable constructor, supporting varargs and primitives */ public static <T> Optional<T> invokeConstructorWithArgs(Class<T> clazz, Object...argsArray) { return invokeConstructorWithArgs(clazz, argsArray, false); } /** Invokes a suitable constructor, supporting varargs and primitives */ public static <T> Optional<T> invokeConstructorWithArgs(Class<T> clazz, Object[] argsArray, boolean setAccessible) { Reflections reflections = new Reflections(clazz.getClassLoader()); return invokeConstructorWithArgs(reflections, clazz, argsArray, setAccessible); } /** Invokes a suitable constructor, supporting varargs and primitives, additionally supporting setAccessible */ @SuppressWarnings("unchecked") public static <T> Optional<T> invokeConstructorWithArgs(Reflections reflections, Class<T> clazz, Object[] argsArray, boolean setAccessible) { 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<argsArray.length; i++) { if (!Boxing.boxedType(varargType).isInstance(argsArray[i]) || (varargType.isPrimitive() && argsArray[i]==null)) { varargsMatch = false; break; } } if (varargsMatch) { Object varargs = Array.newInstance(varargType, argsArray.length+1 - parameterTypes.length); for (int i=parameterTypes.length-1; i<argsArray.length; i++) { Boxing.setInArray(varargs, i+1-parameterTypes.length, argsArray[i], varargType); } Object[] newArgsArray = new Object[parameterTypes.length]; System.arraycopy(argsArray, 0, newArgsArray, 0, parameterTypes.length-1); newArgsArray[parameterTypes.length-1] = varargs; if (setAccessible) constructor.setAccessible(true); return (Optional<T>) Optional.of(reflections.loadInstance(constructor, newArgsArray)); } } } if (typesMatch(argsArray, parameterTypes)) { if (setAccessible) constructor.setAccessible(true); return (Optional<T>) Optional.of(reflections.loadInstance(constructor, 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> T loadInstance(Constructor<T> 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)+": " + Exceptions.collapseText(e), e); } catch (InstantiationException e) { throw new ReflectionAccessException("Failed to create instance of " + constructor.getDeclaringClass() + ": " + Exceptions.collapseText(e), e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to create instance of " + constructor.getDeclaringClass() + ": " + Exceptions.collapseText(e), e); } catch (InvocationTargetException e) { throw new ReflectionAccessException("Failed to create instance of " + constructor.getDeclaringClass() + ": " + Exceptions.collapseText(e), 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() + ": " + Exceptions.collapseText(e), e); } catch (NoSuchMethodException e) { throw new ReflectionNotFoundException("Failed to invoke method " + methodName + " on class " + clazz + " with argument types " + Arrays.asList(argTypes) + ": " + Exceptions.collapseText(e), e); } catch (SecurityException e) { throw new ReflectionAccessException("Failed to invoke method " + methodName + " on class " + clazz + " with argument types " + Arrays.asList(argTypes) + ": " + Exceptions.collapseText(e), 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 + ": " + Exceptions.collapseText(e), 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) + ": " + Exceptions.collapseText(e), e); } catch (InvocationTargetException e) { throw new ReflectionAccessException("Failed to invoke method '" + method.toGenericString() + " on class " + method.getDeclaringClass() + " with argument values " + Arrays.asList(argValues) + ": " + Exceptions.collapseText(e), 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) + ": " + Exceptions.collapseText(e), e); } catch (InvocationTargetException e) { throw new ReflectionAccessException("Failed to invoke method '" + method.toGenericString() + " on class " + method.getDeclaringClass() + " with argument values " + Arrays.asList(argValues) + ": " + Exceptions.collapseText(e), 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<String> 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 + ": " + Exceptions.collapseText(e), e); } catch (IllegalAccessException e) { throw new ReflectionAccessException("Failed to load field '" + field.getName() + " from class " + clazz + ": " + Exceptions.collapseText(e), 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 <T> void copyFields(T source, T target) throws IllegalArgumentException, IllegalAccessException { Class<? extends Object> 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. * <p> * (like a poor man's clone) * <p> * 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; } @SuppressWarnings("unchecked") public static <T> Class<? super T> findSuperType(T impl, String typeName) { Set<Class<?>> toinspect = new LinkedHashSet<Class<?>>(); Set<Class<?>> inspected = new HashSet<Class<?>>(); 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<? super T>) clazz; } inspected.add(clazz); List<Class<?>> 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<Class<?>> getInterfacesIncludingClassAncestors(Class<?> clazz) { Set<Class<?>> result = new LinkedHashSet<Class<?>>(); 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<Field> findPublicFieldsOrderedBySuper(Class<?> clazz) { checkNotNull(clazz, "clazz"); MutableList.Builder<Field> result = MutableList.<Field>builder(); Stack<Class<?>> tovisit = new Stack<Class<?>>(); Set<Class<?>> 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<Field>() { @Override public boolean apply(Field input) { return Modifier.isPublic(input.getModifiers()); }})); } List<Field> resultList = result.build(); Collections.sort(resultList, new Comparator<Field>() { @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<Method> findPublicMethodsOrderedBySuper(Class<?> clazz) { checkNotNull(clazz, "clazz"); MutableList.Builder<Method> result = MutableList.<Method>builder(); Stack<Class<?>> tovisit = new Stack<Class<?>>(); Set<Class<?>> 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<Method>() { @Override public boolean apply(Method input) { return Modifier.isPublic(input.getModifiers()); }})); } List<Method> resultList = result.build(); Collections.sort(resultList, new Comparator<Method>() { @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> T cast(Object candidate, Class<? extends T> 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<Object> invokeMethodWithArgs(Object clazzOrInstance, String method, List<Object> args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { return invokeMethodWithArgs(clazzOrInstance, method, args, false); } public static Optional<Object> invokeMethodWithArgs(Object clazzOrInstance, String method, List<Object> 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<argsArray.length; i++) { if (!Boxing.boxedType(varargType).isInstance(argsArray[i]) || (varargType.isPrimitive() && argsArray[i]==null)) { varargsMatch = false; break; } } if (varargsMatch) { Object varargs = Array.newInstance(varargType, argsArray.length+1 - parameterTypes.length); for (int i=parameterTypes.length-1; i<argsArray.length; i++) { Boxing.setInArray(varargs, i+1-parameterTypes.length, argsArray[i], varargType); } Object[] newArgsArray = new Object[parameterTypes.length]; System.arraycopy(argsArray, 0, newArgsArray, 0, parameterTypes.length-1); newArgsArray[parameterTypes.length-1] = varargs; if (setAccessible) m.setAccessible(true); return Optional.of(m.invoke(instance, newArgsArray)); } } } if (typesMatch(argsArray, parameterTypes)) { if (setAccessible) m.setAccessible(true); return Optional.of(m.invoke(instance, argsArray)); } } } return Optional.absent(); } /** true iff all args match the corresponding types */ public static boolean typesMatch(Object[] argsArray, Class<?>[] 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<lengthRequired; i++) { if (argsArray[i]==null) continue; if (Boxing.boxedType(parameterTypes[i]).isInstance(argsArray[i])) continue; return false; } return true; } /** * Gets all the interfaces implemented by the given type, including its parent classes. * * @param type the class to look up * @return an immutable list of the interface classes */ public static List<Class<?>> getAllInterfaces(@Nullable Class<?> type) { Set<Class<?>> found = Sets.newLinkedHashSet(); findAllInterfaces(type, found); return ImmutableList.copyOf(found); } /** Recurse through the class hierarchies of the type and its interfaces. */ private static void findAllInterfaces(@Nullable Class<?> type, Set<Class<?>> found) { if (type == null) return; for (Class<?> i : type.getInterfaces()) { if (found.add(i)) { // not seen before findAllInterfaces(i, found); } } findAllInterfaces(type.getSuperclass(), found); } public static boolean hasNoArgConstructor(Class<?> clazz) { try { clazz.getConstructor(new Class[0]); return true; } catch (NoSuchMethodException e) { return false; } } public static boolean hasNoNonObjectFields(Class<? extends Object> clazz) { if (Object.class.equals(clazz)) return true; if (clazz.getDeclaredFields().length>0) return false; return hasNoNonObjectFields(clazz.getSuperclass()); } /** Takes a map of old-class-names to renames classes, and returns the mapped name if matched, or absent */ public static Optional<String> tryFindMappedName(Map<String, String> renames, String name) { if (renames==null) return Optional.absent(); String mappedName = renames.get(name); if (mappedName != null) { return Optional.of(mappedName); } // look for inner classes by mapping outer class if (name.contains("$")) { String outerClassName = name.substring(0, name.indexOf('$')); mappedName = renames.get(outerClassName); if (mappedName != null) { return Optional.of(mappedName + name.substring(name.indexOf('$'))); } } return Optional.absent(); } public static String findMappedNameAndLog(Map<String, String> renames, String name) { Optional<String> rename = Reflections.tryFindMappedName(renames, name); if (rename.isPresent()) { LOG.debug("Mapping class '"+name+"' to '"+rename.get()+"'"); return rename.get(); } return name; } }