Last active
February 3, 2023 04:42
-
-
Save Forgo7ten/6573f8e134c2d3d03370cbe0f81a587f to your computer and use it in GitHub Desktop.
Java反射工具类 单文件,提取自XposedHelpers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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