package utils; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * Helpers that simplify hooking and calling methods/constructors, getting and settings fields, ... */ public final class ReflectionUtils { private ReflectionUtils() { } /** * The system class loader which can be used to locate Android framework classes. * Application classes cannot be retrieved from it. * * @see ClassLoader#getSystemClassLoader */ public static final ClassLoader BOOTCLASSLOADER = ClassLoader.getSystemClassLoader(); private static final HashMap fieldCache = new HashMap<>(); private static final HashMap methodCache = new HashMap<>(); private static final HashMap> constructorCache = new HashMap<>(); private static final WeakHashMap> additionalFields = new WeakHashMap<>(); private static final HashMap> sMethodDepth = new HashMap<>(); /** * /** * 寻找并加载Class,如果没有找到则抛出Error * * @param className 全类名 * @return Class对象 * @throws ClassNotFoundError 类没有找到 */ public static Class findClass(String className) { Class clazz = null; try { clazz = ReflectionUtilsInnerClassUtils.getClass(className); } catch (ClassNotFoundException e) { throw new ClassNotFoundError(e); } return clazz; } /** * 寻找并加载Class,如果没有找到则返回null * * @param className 全类名 * @return Class对象或者null */ public static Class findClassIfExists(String className) { try { return findClass(className); } catch (ClassNotFoundError e) { return null; } } /** * Look up a class with the specified class loader. * *

There are various allowed syntaxes for the class name, but it's recommended to use one of * these: *

    *
  • {@code java.lang.String} *
  • {@code java.lang.String[]} (array) *
  • {@code android.app.ActivityThread.ResourcesKey} *
  • {@code android.app.ActivityThread$ResourcesKey} *
* * @param className The class name in one of the formats mentioned above. * @param classLoader The class loader, or {@code null} for the boot class loader. * @return A reference to the class. * @throws ClassNotFoundError In case the class was not found. */ public static Class findClass(String className, ClassLoader classLoader) { if (classLoader == null) classLoader = BOOTCLASSLOADER; try { return ReflectionUtilsInnerClassUtils.getClass(classLoader, className, false); } catch (ClassNotFoundException e) { throw new ClassNotFoundError(e); } } /** * Look up and return a class if it exists. * Like {@link #findClass}, but doesn't throw an exception if the class doesn't exist. * * @param className The class name. * @param classLoader The class loader, or {@code null} for the boot class loader. * @return A reference to the class, or {@code null} if it doesn't exist. */ public static Class findClassIfExists(String className, ClassLoader classLoader) { try { return findClass(className, classLoader); } catch (ClassNotFoundError e) { return null; } } /** * 寻找Field,并设置其可访问,如果找不到则抛出异常 * * @param className 全类名 * @param fieldName 字段名 * @return Field对象 * @throws ClassNotFoundException */ public static Field findField(String className, String fieldName) throws ClassNotFoundException { Class clazz = findClass(className); return findField(clazz, fieldName); } /** * 寻找Field,并设置其可访问,如果找不到则返回null * * @param className 全类名 * @param fieldName 字段名 * @return Field对象或null */ public static Field findFieldIfExists(String className, String fieldName) { Class clazz = null; Field field = null; try { clazz = findClass(className); field = findFieldIfExists(clazz, fieldName); } catch (ClassNotFoundError e) { return null; } return field; } /** * Look up a field in a class and set it to accessible. * * @param clazz The class which either declares or inherits the field. * @param fieldName The field name. * @return A reference to the field. * @throws NoSuchFieldError In case the field was not found. */ public static Field findField(Class clazz, String fieldName) { String fullFieldName = clazz.getName() + '#' + fieldName; if (fieldCache.containsKey(fullFieldName)) { Field field = fieldCache.get(fullFieldName); if (field == null) throw new NoSuchFieldError(fullFieldName); return field; } try { Field field = findFieldRecursiveImpl(clazz, fieldName); field.setAccessible(true); fieldCache.put(fullFieldName, field); return field; } catch (NoSuchFieldException e) { fieldCache.put(fullFieldName, null); throw new NoSuchFieldError(fullFieldName); } } /** * Look up and return a field if it exists. * Like {@link #findField}, but doesn't throw an exception if the field doesn't exist. * * @param clazz The class which either declares or inherits the field. * @param fieldName The field name. * @return A reference to the field, or {@code null} if it doesn't exist. */ public static Field findFieldIfExists(Class clazz, String fieldName) { try { return findField(clazz, fieldName); } catch (NoSuchFieldError e) { return null; } } private static Field findFieldRecursiveImpl(Class clazz, String fieldName) throws NoSuchFieldException { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { while (true) { clazz = clazz.getSuperclass(); if (clazz == null || clazz.equals(Object.class)) break; try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException ignored) { } } throw e; } } /** * Returns the first field of the given type in a class. * Might be useful for Proguard'ed classes to identify fields with unique types. * * @param clazz The class which either declares or inherits the field. * @param type The type of the field. * @return A reference to the first field of the given type. * @throws NoSuchFieldError In case no matching field was not found. */ public static Field findFirstFieldByExactType(Class clazz, Class type) { Class clz = clazz; do { for (Field field : clz.getDeclaredFields()) { if (field.getType() == type) { field.setAccessible(true); return field; } } } while ((clz = clz.getSuperclass()) != null); throw new NoSuchFieldError("Field of type " + type.getName() + " in class " + clazz.getName()); } /** * Look up a method in a class and set it to accessible. * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details. */ public static Method findMethodExact(Class clazz, String methodName, Object... parameterTypes) { return findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypes)); } /** * Look up and return a method if it exists. * See {@link #findMethodExactIfExists(String, ClassLoader, String, Object...)} for details. */ public static Method findMethodExactIfExists(Class clazz, String methodName, Object... parameterTypes) { try { return findMethodExact(clazz, methodName, parameterTypes); } catch (ClassNotFoundError | NoSuchMethodError e) { return null; } } /** * Look up a method in a class and set it to accessible. * The method must be declared or overridden in the given class. * * @param className The name of the class which implements the method. * @param classLoader The class loader for resolving the target and parameter classes. * @param methodName The target method name. * @param parameterTypes The parameter types of the target method. * @return A reference to the method. * @throws NoSuchMethodError In case the method was not found. * @throws ClassNotFoundError In case the target class or one of the parameter types couldn't be resolved. */ public static Method findMethodExact(String className, ClassLoader classLoader, String methodName, Object... parameterTypes) { return findMethodExact(findClass(className, classLoader), methodName, getParameterClasses(classLoader, parameterTypes)); } /** * Look up and return a method if it exists. * Like {@link #findMethodExact(String, ClassLoader, String, Object...)}, but doesn't throw an * exception if the method doesn't exist. * * @param className The name of the class which implements the method. * @param classLoader The class loader for resolving the target and parameter classes. * @param methodName The target method name. * @param parameterTypes The parameter types of the target method. * @return A reference to the method, or {@code null} if it doesn't exist. */ public static Method findMethodExactIfExists(String className, ClassLoader classLoader, String methodName, Object... parameterTypes) { try { return findMethodExact(className, classLoader, methodName, parameterTypes); } catch (ClassNotFoundError | NoSuchMethodError e) { return null; } } /** * Look up a method in a class and set it to accessible. * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details. * *

This variant requires that you already have reference to all the parameter types. */ public static Method findMethodExact(Class clazz, String methodName, Class... parameterTypes) { String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#exact"; if (methodCache.containsKey(fullMethodName)) { Method method = methodCache.get(fullMethodName); if (method == null) throw new NoSuchMethodError(fullMethodName); return method; } try { Method method = clazz.getDeclaredMethod(methodName, parameterTypes); method.setAccessible(true); methodCache.put(fullMethodName, method); return method; } catch (NoSuchMethodException e) { methodCache.put(fullMethodName, null); throw new NoSuchMethodError(fullMethodName); } } /** * Returns an array of all methods declared/overridden in a class with the specified parameter types. * *

The return type is optional, it will not be compared if it is {@code null}. * Use {@code void.class} if you want to search for methods returning nothing. * * @param clazz The class to look in. * @param returnType The return type, or {@code null} (see above). * @param parameterTypes The parameter types. * @return An array with matching methods, all set to accessible already. */ public static Method[] findMethodsByExactParameters(Class clazz, Class returnType, Class... parameterTypes) { List result = new LinkedList<>(); for (Method method : clazz.getDeclaredMethods()) { if (returnType != null && returnType != method.getReturnType()) continue; Class[] methodParameterTypes = method.getParameterTypes(); if (parameterTypes.length != methodParameterTypes.length) continue; boolean match = true; for (int i = 0; i < parameterTypes.length; i++) { if (parameterTypes[i] != methodParameterTypes[i]) { match = false; break; } } if (!match) continue; method.setAccessible(true); result.add(method); } return result.toArray(new Method[result.size()]); } /** * Look up a method in a class and set it to accessible. * *

This does'nt only look for exact matches, but for the best match. All considered candidates * must be compatible with the given parameter types, i.e. the parameters must be assignable * to the method's formal parameters. Inherited methods are considered here. * * @param clazz The class which declares, inherits or overrides the method. * @param methodName The method name. * @param parameterTypes The types of the method's parameters. * @return A reference to the best-matching method. * @throws NoSuchMethodError In case no suitable method was found. */ public static Method findMethodBestMatch(Class clazz, String methodName, Class... parameterTypes) { String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#bestmatch"; if (methodCache.containsKey(fullMethodName)) { Method method = methodCache.get(fullMethodName); if (method == null) throw new NoSuchMethodError(fullMethodName); return method; } try { Method method = findMethodExact(clazz, methodName, parameterTypes); methodCache.put(fullMethodName, method); return method; } catch (NoSuchMethodError ignored) { } Method bestMatch = null; Class clz = clazz; boolean considerPrivateMethods = true; do { for (Method method : clz.getDeclaredMethods()) { // don't consider private methods of superclasses if (!considerPrivateMethods && Modifier.isPrivate(method.getModifiers())) continue; // compare name and parameters if (method.getName().equals(methodName) && ReflectionUtilsInnerClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) { // get accessible version of method if (bestMatch == null || ReflectionUtilsInnerMemberUtils.compareParameterTypes(method.getParameterTypes(), bestMatch.getParameterTypes(), parameterTypes) < 0) { bestMatch = method; } } } considerPrivateMethods = false; } while ((clz = clz.getSuperclass()) != null); if (bestMatch != null) { bestMatch.setAccessible(true); methodCache.put(fullMethodName, bestMatch); return bestMatch; } else { NoSuchMethodError e = new NoSuchMethodError(fullMethodName); methodCache.put(fullMethodName, null); throw e; } } /** * Look up a method in a class and set it to accessible. * *

See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant * determines the parameter types from the classes of the given objects. */ public static Method findMethodBestMatch(Class clazz, String methodName, Object... args) { return findMethodBestMatch(clazz, methodName, getParameterTypes(args)); } /** * Look up a method in a class and set it to accessible. * *

See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant * determines the parameter types from the classes of the given objects. For any item that is * {@code null}, the type is taken from {@code parameterTypes} instead. */ public static Method findMethodBestMatch(Class clazz, String methodName, Class[] parameterTypes, Object[] args) { Class[] argsClasses = null; for (int i = 0; i < parameterTypes.length; i++) { if (parameterTypes[i] != null) continue; if (argsClasses == null) argsClasses = getParameterTypes(args); parameterTypes[i] = argsClasses[i]; } return findMethodBestMatch(clazz, methodName, parameterTypes); } /** * Returns an array with the classes of the given objects. */ public static Class[] getParameterTypes(Object... args) { Class[] clazzes = new Class[args.length]; for (int i = 0; i < args.length; i++) { clazzes[i] = (args[i] != null) ? args[i].getClass() : null; } return clazzes; } /** * Retrieve classes from an array, where each element might either be a Class * already, or a String with the full class name. */ private static Class[] getParameterClasses(ClassLoader classLoader, Object[] parameterTypesAndCallback) { Class[] parameterClasses = null; for (int i = parameterTypesAndCallback.length - 1; i >= 0; i--) { Object type = parameterTypesAndCallback[i]; if (type == null) throw new ClassNotFoundError("parameter type must not be null", null); // ignore trailing callback if (parameterClasses == null) parameterClasses = new Class[i + 1]; if (type instanceof Class) parameterClasses[i] = (Class) type; else if (type instanceof String) parameterClasses[i] = findClass((String) type, classLoader); else throw new ClassNotFoundError("parameter type must either be specified as Class or String", null); } // if there are no arguments for the method if (parameterClasses == null) parameterClasses = new Class[0]; return parameterClasses; } /** * Returns an array of the given classes. */ public static Class[] getClassesAsArray(Class... clazzes) { return clazzes; } private static String getParametersString(Class... clazzes) { StringBuilder sb = new StringBuilder("("); boolean first = true; for (Class clazz : clazzes) { if (first) first = false; else sb.append(","); if (clazz != null) sb.append(clazz.getCanonicalName()); else sb.append("null"); } sb.append(")"); return sb.toString(); } /** * Look up a constructor of a class and set it to accessible. * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details. */ public static Constructor findConstructorExact(Class clazz, Object... parameterTypes) { return findConstructorExact(clazz, getParameterClasses(clazz.getClassLoader(), parameterTypes)); } /** * Look up and return a constructor if it exists. * See {@link #findMethodExactIfExists(String, ClassLoader, String, Object...)} for details. */ public static Constructor findConstructorExactIfExists(Class clazz, Object... parameterTypes) { try { return findConstructorExact(clazz, parameterTypes); } catch (ClassNotFoundError | NoSuchMethodError e) { return null; } } /** * Look up a constructor of a class and set it to accessible. * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details. */ public static Constructor findConstructorExact(String className, ClassLoader classLoader, Object... parameterTypes) { return findConstructorExact(findClass(className, classLoader), getParameterClasses(classLoader, parameterTypes)); } /** * Look up and return a constructor if it exists. * See {@link #findMethodExactIfExists(String, ClassLoader, String, Object...)} for details. */ public static Constructor findConstructorExactIfExists(String className, ClassLoader classLoader, Object... parameterTypes) { try { return findConstructorExact(className, classLoader, parameterTypes); } catch (ClassNotFoundError | NoSuchMethodError e) { return null; } } /** * Look up a constructor of a class and set it to accessible. * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details. */ public static Constructor findConstructorExact(Class clazz, Class... parameterTypes) { String fullConstructorName = clazz.getName() + getParametersString(parameterTypes) + "#exact"; if (constructorCache.containsKey(fullConstructorName)) { Constructor constructor = constructorCache.get(fullConstructorName); if (constructor == null) throw new NoSuchMethodError(fullConstructorName); return constructor; } try { Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); constructor.setAccessible(true); constructorCache.put(fullConstructorName, constructor); return constructor; } catch (NoSuchMethodException e) { constructorCache.put(fullConstructorName, null); throw new NoSuchMethodError(fullConstructorName); } } /** * Look up a constructor in a class and set it to accessible. * *

See {@link #findMethodBestMatch(Class, String, Class...)} for details. */ public static Constructor findConstructorBestMatch(Class clazz, Class... parameterTypes) { String fullConstructorName = clazz.getName() + getParametersString(parameterTypes) + "#bestmatch"; if (constructorCache.containsKey(fullConstructorName)) { Constructor constructor = constructorCache.get(fullConstructorName); if (constructor == null) throw new NoSuchMethodError(fullConstructorName); return constructor; } try { Constructor constructor = findConstructorExact(clazz, parameterTypes); constructorCache.put(fullConstructorName, constructor); return constructor; } catch (NoSuchMethodError ignored) { } Constructor bestMatch = null; Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors) { // compare name and parameters if (ReflectionUtilsInnerClassUtils.isAssignable(parameterTypes, constructor.getParameterTypes(), true)) { // get accessible version of method if (bestMatch == null || ReflectionUtilsInnerMemberUtils.compareParameterTypes(constructor.getParameterTypes(), bestMatch.getParameterTypes(), parameterTypes) < 0) { bestMatch = constructor; } } } if (bestMatch != null) { bestMatch.setAccessible(true); constructorCache.put(fullConstructorName, bestMatch); return bestMatch; } else { NoSuchMethodError e = new NoSuchMethodError(fullConstructorName); constructorCache.put(fullConstructorName, null); throw e; } } /** * Look up a constructor in a class and set it to accessible. * *

See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant * determines the parameter types from the classes of the given objects. */ public static Constructor findConstructorBestMatch(Class clazz, Object... args) { return findConstructorBestMatch(clazz, getParameterTypes(args)); } /** * Look up a constructor in a class and set it to accessible. * *

See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant * determines the parameter types from the classes of the given objects. For any item that is * {@code null}, the type is taken from {@code parameterTypes} instead. */ public static Constructor findConstructorBestMatch(Class clazz, Class[] parameterTypes, Object[] args) { Class[] argsClasses = null; for (int i = 0; i < parameterTypes.length; i++) { if (parameterTypes[i] != null) continue; if (argsClasses == null) argsClasses = getParameterTypes(args); parameterTypes[i] = argsClasses[i]; } return findConstructorBestMatch(clazz, parameterTypes); } /** * Thrown when a class loader is unable to find a class. Unlike {@link ClassNotFoundException}, * callers are not forced to explicitly catch this. If uncaught, the error will be passed to the * next caller in the stack. */ public static final class ClassNotFoundError extends Error { private static final long serialVersionUID = -1070936889459514628L; /** * @hide */ public ClassNotFoundError(Throwable cause) { super(cause); } /** * @hide */ public ClassNotFoundError(String detailMessage, Throwable cause) { super(detailMessage, cause); } } /** * Returns the index of the first parameter declared with the given type. * * @throws NoSuchFieldError if there is no parameter with that type. * @hide */ public static int getFirstParameterIndexByType(Member method, Class type) { Class[] classes = (method instanceof Method) ? ((Method) method).getParameterTypes() : ((Constructor) method).getParameterTypes(); for (int i = 0; i < classes.length; i++) { if (classes[i] == type) { return i; } } throw new NoSuchFieldError("No parameter of type " + type + " found in " + method); } /** * Returns the index of the parameter declared with the given type, ensuring that there is exactly one such parameter. * * @throws NoSuchFieldError if there is no or more than one parameter with that type. * @hide */ public static int getParameterIndexByType(Member method, Class type) { Class[] classes = (method instanceof Method) ? ((Method) method).getParameterTypes() : ((Constructor) method).getParameterTypes(); int idx = -1; for (int i = 0; i < classes.length; i++) { if (classes[i] == type) { if (idx == -1) { idx = i; } else { throw new NoSuchFieldError("More than one parameter of type " + type + " found in " + method); } } } if (idx != -1) { return idx; } else { throw new NoSuchFieldError("No parameter of type " + type + " found in " + method); } } //################################################################################################# /** * Sets the value of an object field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setObjectField(Object obj, String fieldName, Object value) { try { findField(obj.getClass(), fieldName).set(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code boolean} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setBooleanField(Object obj, String fieldName, boolean value) { try { findField(obj.getClass(), fieldName).setBoolean(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code byte} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setByteField(Object obj, String fieldName, byte value) { try { findField(obj.getClass(), fieldName).setByte(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code char} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setCharField(Object obj, String fieldName, char value) { try { findField(obj.getClass(), fieldName).setChar(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code double} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setDoubleField(Object obj, String fieldName, double value) { try { findField(obj.getClass(), fieldName).setDouble(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code float} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setFloatField(Object obj, String fieldName, float value) { try { findField(obj.getClass(), fieldName).setFloat(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of an {@code int} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setIntField(Object obj, String fieldName, int value) { try { findField(obj.getClass(), fieldName).setInt(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code long} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setLongField(Object obj, String fieldName, long value) { try { findField(obj.getClass(), fieldName).setLong(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a {@code short} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static void setShortField(Object obj, String fieldName, short value) { try { findField(obj.getClass(), fieldName).setShort(obj, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } //################################################################################################# /** * Returns the value of an object field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static Object getObjectField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).get(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * For inner classes, returns the surrounding instance, i.e. the {@code this} reference of the surrounding class. */ public static Object getSurroundingThis(Object obj) { return getObjectField(obj, "this$0"); } /** * Returns the value of a {@code boolean} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") public static boolean getBooleanField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getBoolean(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a {@code byte} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static byte getByteField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getByte(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a {@code char} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static char getCharField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getChar(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a {@code double} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static double getDoubleField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getDouble(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a {@code float} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static float getFloatField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getFloat(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of an {@code int} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static int getIntField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getInt(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a {@code long} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static long getLongField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getLong(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a {@code short} field in the given object instance. A class reference is not sufficient! See also {@link #findField}. */ public static short getShortField(Object obj, String fieldName) { try { return findField(obj.getClass(), fieldName).getShort(obj); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } //################################################################################################# /** * Sets the value of a static object field in the given class. See also {@link #findField}. */ public static void setStaticObjectField(Class clazz, String fieldName, Object value) { try { findField(clazz, fieldName).set(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code boolean} field in the given class. See also {@link #findField}. */ public static void setStaticBooleanField(Class clazz, String fieldName, boolean value) { try { findField(clazz, fieldName).setBoolean(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code byte} field in the given class. See also {@link #findField}. */ public static void setStaticByteField(Class clazz, String fieldName, byte value) { try { findField(clazz, fieldName).setByte(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code char} field in the given class. See also {@link #findField}. */ public static void setStaticCharField(Class clazz, String fieldName, char value) { try { findField(clazz, fieldName).setChar(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code double} field in the given class. See also {@link #findField}. */ public static void setStaticDoubleField(Class clazz, String fieldName, double value) { try { findField(clazz, fieldName).setDouble(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code float} field in the given class. See also {@link #findField}. */ public static void setStaticFloatField(Class clazz, String fieldName, float value) { try { findField(clazz, fieldName).setFloat(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code int} field in the given class. See also {@link #findField}. */ public static void setStaticIntField(Class clazz, String fieldName, int value) { try { findField(clazz, fieldName).setInt(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code long} field in the given class. See also {@link #findField}. */ public static void setStaticLongField(Class clazz, String fieldName, long value) { try { findField(clazz, fieldName).setLong(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code short} field in the given class. See also {@link #findField}. */ public static void setStaticShortField(Class clazz, String fieldName, short value) { try { findField(clazz, fieldName).setShort(null, value); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } //################################################################################################# /** * Returns the value of a static object field in the given class. See also {@link #findField}. */ public static Object getStaticObjectField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).get(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Returns the value of a static {@code boolean} field in the given class. See also {@link #findField}. */ public static boolean getStaticBooleanField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getBoolean(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code byte} field in the given class. See also {@link #findField}. */ public static byte getStaticByteField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getByte(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code char} field in the given class. See also {@link #findField}. */ public static char getStaticCharField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getChar(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code double} field in the given class. See also {@link #findField}. */ public static double getStaticDoubleField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getDouble(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code float} field in the given class. See also {@link #findField}. */ public static float getStaticFloatField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getFloat(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code int} field in the given class. See also {@link #findField}. */ public static int getStaticIntField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getInt(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code long} field in the given class. See also {@link #findField}. */ public static long getStaticLongField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getLong(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } /** * Sets the value of a static {@code short} field in the given class. See also {@link #findField}. */ public static short getStaticShortField(Class clazz, String fieldName) { try { return findField(clazz, fieldName).getShort(null); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } } //################################################################################################# /** * Calls an instance or static method of the given object. * The method is resolved using {@link #findMethodBestMatch(Class, String, Object...)}. * * @param obj The object instance. A class reference is not sufficient! * @param methodName The method name. * @param args The arguments for the method call. * @throws NoSuchMethodError In case no suitable method was found. * @throws InvocationTargetError In case an exception was thrown by the invoked method. */ public static Object callMethod(Object obj, String methodName, Object... args) { try { return findMethodBestMatch(obj.getClass(), methodName, args).invoke(obj, args); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new InvocationTargetError(e.getCause()); } } /** * Calls an instance or static method of the given object. * See {@link #callMethod(Object, String, Object...)}. * *

This variant allows you to specify parameter types, which can help in case there are multiple * methods with the same name, especially if you call it with {@code null} parameters. */ public static Object callMethod(Object obj, String methodName, Class[] parameterTypes, Object... args) { try { return findMethodBestMatch(obj.getClass(), methodName, parameterTypes, args).invoke(obj, args); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new InvocationTargetError(e.getCause()); } } /** * Calls a static method of the given class. * The method is resolved using {@link #findMethodBestMatch(Class, String, Object...)}. * * @param clazz The class reference. * @param methodName The method name. * @param args The arguments for the method call. * @throws NoSuchMethodError In case no suitable method was found. * @throws InvocationTargetError In case an exception was thrown by the invoked method. */ public static Object callStaticMethod(Class clazz, String methodName, Object... args) { try { return findMethodBestMatch(clazz, methodName, args).invoke(null, args); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new InvocationTargetError(e.getCause()); } } /** * Calls a static method of the given class. * See {@link #callStaticMethod(Class, String, Object...)}. * *

This variant allows you to specify parameter types, which can help in case there are multiple * methods with the same name, especially if you call it with {@code null} parameters. */ public static Object callStaticMethod(Class clazz, String methodName, Class[] parameterTypes, Object... args) { try { return findMethodBestMatch(clazz, methodName, parameterTypes, args).invoke(null, args); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new InvocationTargetError(e.getCause()); } } /** * This class provides a wrapper for an exception thrown by a method invocation. * * @see #callMethod(Object, String, Object...) * @see #callStaticMethod(Class, String, Object...) * @see #newInstance(Class, Object...) */ public static final class InvocationTargetError extends Error { private static final long serialVersionUID = -1070936889459514628L; /** * @hide */ public InvocationTargetError(Throwable cause) { super(cause); } } //################################################################################################# /** * Creates a new instance of the given class. * The constructor is resolved using {@link #findConstructorBestMatch(Class, Object...)}. * * @param clazz The class reference. * @param args The arguments for the constructor call. * @throws NoSuchMethodError In case no suitable constructor was found. * @throws InvocationTargetError In case an exception was thrown by the invoked method. * @throws InstantiationError In case the class cannot be instantiated. */ public static Object newInstance(Class clazz, Object... args) { try { return findConstructorBestMatch(clazz, args).newInstance(args); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new InvocationTargetError(e.getCause()); } catch (InstantiationException e) { throw new InstantiationError(e.getMessage()); } } /** * Creates a new instance of the given class. * See {@link #newInstance(Class, Object...)}. * *

This variant allows you to specify parameter types, which can help in case there are multiple * constructors with the same name, especially if you call it with {@code null} parameters. */ public static Object newInstance(Class clazz, Class[] parameterTypes, Object... args) { try { return findConstructorBestMatch(clazz, parameterTypes, args).newInstance(args); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new InvocationTargetError(e.getCause()); } catch (InstantiationException e) { throw new InstantiationError(e.getMessage()); } } //################################################################################################# /** * Attaches any value to an object instance. This simulates adding an instance field. * The value can be retrieved again with {@link #getAdditionalInstanceField}. * * @param obj The object instance for which the value should be stored. * @param key The key in the value map for this object instance. * @param value The value to store. * @return The previously stored value for this instance/key combination, or {@code null} if there was none. */ public static Object setAdditionalInstanceField(Object obj, String key, Object value) { if (obj == null) throw new NullPointerException("object must not be null"); if (key == null) throw new NullPointerException("key must not be null"); HashMap objectFields; synchronized (additionalFields) { objectFields = additionalFields.get(obj); if (objectFields == null) { objectFields = new HashMap<>(); additionalFields.put(obj, objectFields); } } synchronized (objectFields) { return objectFields.put(key, value); } } /** * Returns a value which was stored with {@link #setAdditionalInstanceField}. * * @param obj The object instance for which the value has been stored. * @param key The key in the value map for this object instance. * @return The stored value for this instance/key combination, or {@code null} if there is none. */ public static Object getAdditionalInstanceField(Object obj, String key) { if (obj == null) throw new NullPointerException("object must not be null"); if (key == null) throw new NullPointerException("key must not be null"); HashMap objectFields; synchronized (additionalFields) { objectFields = additionalFields.get(obj); if (objectFields == null) return null; } synchronized (objectFields) { return objectFields.get(key); } } /** * Removes and returns a value which was stored with {@link #setAdditionalInstanceField}. * * @param obj The object instance for which the value has been stored. * @param key The key in the value map for this object instance. * @return The previously stored value for this instance/key combination, or {@code null} if there was none. */ public static Object removeAdditionalInstanceField(Object obj, String key) { if (obj == null) throw new NullPointerException("object must not be null"); if (key == null) throw new NullPointerException("key must not be null"); HashMap objectFields; synchronized (additionalFields) { objectFields = additionalFields.get(obj); if (objectFields == null) return null; } synchronized (objectFields) { return objectFields.remove(key); } } /** * Like {@link #setAdditionalInstanceField}, but the value is stored for the class of {@code obj}. */ public static Object setAdditionalStaticField(Object obj, String key, Object value) { return setAdditionalInstanceField(obj.getClass(), key, value); } /** * Like {@link #getAdditionalInstanceField}, but the value is returned for the class of {@code obj}. */ public static Object getAdditionalStaticField(Object obj, String key) { return getAdditionalInstanceField(obj.getClass(), key); } /** * Like {@link #removeAdditionalInstanceField}, but the value is removed and returned for the class of {@code obj}. */ public static Object removeAdditionalStaticField(Object obj, String key) { return removeAdditionalInstanceField(obj.getClass(), key); } /** * Like {@link #setAdditionalInstanceField}, but the value is stored for {@code clazz}. */ public static Object setAdditionalStaticField(Class clazz, String key, Object value) { return setAdditionalInstanceField(clazz, key, value); } /** * Like {@link #setAdditionalInstanceField}, but the value is returned for {@code clazz}. */ public static Object getAdditionalStaticField(Class clazz, String key) { return getAdditionalInstanceField(clazz, key); } /** * Like {@link #setAdditionalInstanceField}, but the value is removed and returned for {@code clazz}. */ public static Object removeAdditionalStaticField(Class clazz, String key) { return removeAdditionalInstanceField(clazz, key); } //################################################################################################# /** * Increments the depth counter for the given method. * * @param method The method name. Should be prefixed with a unique, module-specific string. * @return The updated depth. */ public static int incrementMethodDepth(String method) { return getMethodDepthCounter(method).get().incrementAndGet(); } /** * Decrements the depth counter for the given method. * See {@link #incrementMethodDepth} for details. * * @param method The method name. Should be prefixed with a unique, module-specific string. * @return The updated depth. */ public static int decrementMethodDepth(String method) { return getMethodDepthCounter(method).get().decrementAndGet(); } /** * Returns the current depth counter for the given method. * See {@link #incrementMethodDepth} for details. * * @param method The method name. Should be prefixed with a unique, module-specific string. * @return The updated depth. */ public static int getMethodDepth(String method) { return getMethodDepthCounter(method).get().get(); } private static ThreadLocal getMethodDepthCounter(String method) { synchronized (sMethodDepth) { ThreadLocal counter = sMethodDepth.get(method); if (counter == null) { counter = new ThreadLocal() { @Override protected AtomicInteger initialValue() { return new AtomicInteger(); } }; sMethodDepth.put(method, counter); } return counter; } } //################################################################################################# /** * Returns the method that is overridden by the given method. * It returns {@code null} if the method doesn't override another method or if that method is * abstract, i.e. if this is the first implementation in the hierarchy. */ /*package*/ static Method getOverriddenMethod(Method method) { int modifiers = method.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers)) { return null; } String name = method.getName(); Class[] parameters = method.getParameterTypes(); Class clazz = method.getDeclaringClass().getSuperclass(); while (clazz != null) { try { Method superMethod = clazz.getDeclaredMethod(name, parameters); modifiers = superMethod.getModifiers(); if (!Modifier.isPrivate(modifiers) && !Modifier.isAbstract(modifiers)) { return superMethod; } else { return null; } } catch (NoSuchMethodException ignored) { clazz = clazz.getSuperclass(); } } return null; } /** * Returns all methods which this class overrides. */ /*package*/ static Set getOverriddenMethods(Class clazz) { Set methods = new HashSet<>(); for (Method method : clazz.getDeclaredMethods()) { Method overridden = getOverriddenMethod(method); if (overridden != null) { methods.add(overridden); } } return methods; } } class ReflectionUtilsInnerClassUtils { public static final char PACKAGE_SEPARATOR_CHAR = '.'; public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; private static final Map abbreviationMap = new HashMap(); private static final Map, Class> primitiveWrapperMap = new HashMap, Class>(); static { primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); primitiveWrapperMap.put(Byte.TYPE, Byte.class); primitiveWrapperMap.put(Character.TYPE, Character.class); primitiveWrapperMap.put(Short.TYPE, Short.class); primitiveWrapperMap.put(Integer.TYPE, Integer.class); primitiveWrapperMap.put(Long.TYPE, Long.class); primitiveWrapperMap.put(Double.TYPE, Double.class); primitiveWrapperMap.put(Float.TYPE, Float.class); primitiveWrapperMap.put(Void.TYPE, Void.TYPE); } /** * Maps wrapper {@code Class}es to their corresponding primitive types. */ private static final Map, Class> wrapperPrimitiveMap = new HashMap, Class>(); static { for (Class primitiveClass : primitiveWrapperMap.keySet()) { Class wrapperClass = primitiveWrapperMap.get(primitiveClass); if (!primitiveClass.equals(wrapperClass)) { wrapperPrimitiveMap.put(wrapperClass, primitiveClass); } } } public static Class getClass(ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException { try { Class clazz; if (abbreviationMap.containsKey(className)) { String clsName = "[" + abbreviationMap.get(className); clazz = Class.forName(clsName, initialize, classLoader).getComponentType(); } else { clazz = Class.forName(toCanonicalName(className), initialize, classLoader); } return clazz; } catch (ClassNotFoundException ex) { // allow path separators (.) as inner class name separators int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); if (lastDotIndex != -1) { try { return getClass(classLoader, className.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), initialize); } catch (ClassNotFoundException ex2) { // NOPMD // ignore exception } } throw ex; } } public static Class getClass(ClassLoader classLoader, String className) throws ClassNotFoundException { return getClass(classLoader, className, true); } public static Class getClass(String className) throws ClassNotFoundException { return getClass(className, true); } public static Class getClass(String className, boolean initialize) throws ClassNotFoundException { ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); ClassLoader loader = contextCL == null ? ReflectionUtilsInnerClassUtils.class.getClassLoader() : contextCL; return getClass(loader, className, initialize); } public static boolean isAssignable(Class[] classArray, Class[] toClassArray, boolean autoboxing) { if (ReflectionUtilsInnerArrayUtils.isSameLength(classArray, toClassArray) == false) { return false; } if (classArray == null) { classArray = ReflectionUtilsInnerArrayUtils.EMPTY_CLASS_ARRAY; } if (toClassArray == null) { toClassArray = ReflectionUtilsInnerArrayUtils.EMPTY_CLASS_ARRAY; } for (int i = 0; i < classArray.length; i++) { if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) { return false; } } return true; } public static boolean isAssignable(Class cls, Class toClass) { return isAssignable(cls, toClass, ReflectionUtilsInnerSystemUtils.isJavaVersionAtLeast(ReflectionUtilsInnerJavaVersion.JAVA_1_5)); } public static boolean isAssignable(Class cls, Class toClass, boolean autoboxing) { if (toClass == null) { return false; } // have to check for null, as isAssignableFrom doesn't if (cls == null) { return !toClass.isPrimitive(); } //autoboxing: if (autoboxing) { if (cls.isPrimitive() && !toClass.isPrimitive()) { cls = primitiveToWrapper(cls); if (cls == null) { return false; } } if (toClass.isPrimitive() && !cls.isPrimitive()) { cls = wrapperToPrimitive(cls); if (cls == null) { return false; } } } if (cls.equals(toClass)) { return true; } if (cls.isPrimitive()) { if (toClass.isPrimitive() == false) { return false; } if (Integer.TYPE.equals(cls)) { return Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Long.TYPE.equals(cls)) { return Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Boolean.TYPE.equals(cls)) { return false; } if (Double.TYPE.equals(cls)) { return false; } if (Float.TYPE.equals(cls)) { return Double.TYPE.equals(toClass); } if (Character.TYPE.equals(cls)) { return Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Short.TYPE.equals(cls)) { return Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Byte.TYPE.equals(cls)) { return Short.TYPE.equals(toClass) || Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } return false; } return toClass.isAssignableFrom(cls); } public static Class primitiveToWrapper(Class cls) { Class convertedClass = cls; if (cls != null && cls.isPrimitive()) { convertedClass = primitiveWrapperMap.get(cls); } return convertedClass; } private static String toCanonicalName(String className) { className = ReflectionUtilsInnerStringUtils.deleteWhitespace(className); if (className == null) { throw new NullPointerException("className must not be null."); } else if (className.endsWith("[]")) { StringBuilder classNameBuffer = new StringBuilder(); while (className.endsWith("[]")) { className = className.substring(0, className.length() - 2); classNameBuffer.append("["); } String abbreviation = abbreviationMap.get(className); if (abbreviation != null) { classNameBuffer.append(abbreviation); } else { classNameBuffer.append("L").append(className).append(";"); } className = classNameBuffer.toString(); } return className; } public static Class wrapperToPrimitive(Class cls) { return wrapperPrimitiveMap.get(cls); } } class ReflectionUtilsInnerMemberUtils { private static final Class[] ORDERED_PRIMITIVE_TYPES = {Byte.TYPE, Short.TYPE, Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE}; public static int compareParameterTypes(Class[] left, Class[] right, Class[] actual) { float leftCost = getTotalTransformationCost(actual, left); float rightCost = getTotalTransformationCost(actual, right); return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0; } private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) { float totalCost = 0.0f; for (int i = 0; i < srcArgs.length; i++) { Class srcClass, destClass; srcClass = srcArgs[i]; destClass = destArgs[i]; totalCost += getObjectTransformationCost(srcClass, destClass); } return totalCost; } private static float getObjectTransformationCost(Class srcClass, Class destClass) { if (destClass.isPrimitive()) { return getPrimitivePromotionCost(srcClass, destClass); } float cost = 0.0f; while (srcClass != null && !destClass.equals(srcClass)) { if (destClass.isInterface() && ReflectionUtilsInnerClassUtils.isAssignable(srcClass, destClass)) { cost += 0.25f; break; } cost++; srcClass = srcClass.getSuperclass(); } if (srcClass == null) { cost += 1.5f; } return cost; } private static float getPrimitivePromotionCost(final Class srcClass, final Class destClass) { float cost = 0.0f; Class cls = srcClass; if (!cls.isPrimitive()) { cost += 0.1f; cls = ReflectionUtilsInnerClassUtils.wrapperToPrimitive(cls); } for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) { if (cls == ORDERED_PRIMITIVE_TYPES[i]) { cost += 0.1f; if (i < ORDERED_PRIMITIVE_TYPES.length - 1) { cls = ORDERED_PRIMITIVE_TYPES[i + 1]; } } } return cost; } } class ReflectionUtilsInnerArrayUtils { public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; public static boolean isSameLength(Object[] array1, Object[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { return false; } return true; } } class ReflectionUtilsInnerStringUtils { public static String deleteWhitespace(String str) { if (isEmpty(str)) { return str; } int sz = str.length(); char[] chs = new char[sz]; int count = 0; for (int i = 0; i < sz; i++) { if (!Character.isWhitespace(str.charAt(i))) { chs[count++] = str.charAt(i); } } if (count == sz) { return str; } return new String(chs, 0, count); } public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } } class ReflectionUtilsInnerSystemUtils { public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version"); private static final ReflectionUtilsInnerJavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = ReflectionUtilsInnerJavaVersion.get(JAVA_SPECIFICATION_VERSION); public static boolean isJavaVersionAtLeast(ReflectionUtilsInnerJavaVersion requiredVersion) { return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion); } private static String getSystemProperty(String property) { try { return System.getProperty(property); } catch (SecurityException ex) { // we are not allowed to look at this property System.err.println("Caught a SecurityException reading the system property '" + property + "'; the SystemUtils property value will default to null."); return null; } } } enum ReflectionUtilsInnerJavaVersion { JAVA_0_9(1.5f, "0.9"), JAVA_1_1(1.1f, "1.1"), JAVA_1_2(1.2f, "1.2"), JAVA_1_3(1.3f, "1.3"), JAVA_1_4(1.4f, "1.4"), JAVA_1_5(1.5f, "1.5"), JAVA_1_6(1.6f, "1.6"), JAVA_1_7(1.7f, "1.7"), JAVA_1_8(1.8f, "1.8"); private float value; private String name; ReflectionUtilsInnerJavaVersion(final float value, final String name) { this.value = value; this.name = name; } public boolean atLeast(ReflectionUtilsInnerJavaVersion requiredVersion) { return this.value >= requiredVersion.value; } static ReflectionUtilsInnerJavaVersion get(final String nom) { if ("0.9".equals(nom)) { return JAVA_0_9; } else if ("1.1".equals(nom)) { return JAVA_1_1; } else if ("1.2".equals(nom)) { return JAVA_1_2; } else if ("1.3".equals(nom)) { return JAVA_1_3; } else if ("1.4".equals(nom)) { return JAVA_1_4; } else if ("1.5".equals(nom)) { return JAVA_1_5; } else if ("1.6".equals(nom)) { return JAVA_1_6; } else if ("1.7".equals(nom)) { return JAVA_1_7; } else if ("1.8".equals(nom)) { return JAVA_1_8; } else { return null; } } @Override public String toString() { return name; } }