Skip to content

Instantly share code, notes, and snippets.

@Forgo7ten
Last active February 3, 2023 04:42
Show Gist options
  • Save Forgo7ten/6573f8e134c2d3d03370cbe0f81a587f to your computer and use it in GitHub Desktop.
Save Forgo7ten/6573f8e134c2d3d03370cbe0f81a587f to your computer and use it in GitHub Desktop.
Java反射工具类 单文件,提取自XposedHelpers
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<String, Field> fieldCache = new HashMap<>();
private static final HashMap<String, Method> methodCache = new HashMap<>();
private static final HashMap<String, Constructor<?>> constructorCache = new HashMap<>();
private static final WeakHashMap<Object, HashMap<String, Object>> additionalFields = new WeakHashMap<>();
private static final HashMap<String, ThreadLocal<AtomicInteger>> 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.
*
* <p>There are various allowed syntaxes for the class name, but it's recommended to use one of
* these:
* <ul>
* <li>{@code java.lang.String}
* <li>{@code java.lang.String[]} (array)
* <li>{@code android.app.ActivityThread.ResourcesKey}
* <li>{@code android.app.ActivityThread$ResourcesKey}
* </ul>
*
* @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.
*
* <p>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.
*
* <p>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<Method> 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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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...)}.
*
* <p>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...)}.
*
* <p>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...)}.
*
* <p>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<String, Object> 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<String, Object> 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<String, Object> 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<AtomicInteger> getMethodDepthCounter(String method) {
synchronized (sMethodDepth) {
ThreadLocal<AtomicInteger> counter = sMethodDepth.get(method);
if (counter == null) {
counter = new ThreadLocal<AtomicInteger>() {
@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<Method> getOverriddenMethods(Class<?> clazz) {
Set<Method> 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<String, String> abbreviationMap = new HashMap<String, String>();
private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, 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<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, 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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment