Plex-FAWE/worldedit-core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java

739 lines
24 KiB
Java
Raw Normal View History

package com.boydti.fawe.util;
2020-02-22 03:53:25 +00:00
import java.lang.invoke.MethodHandles;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
2019-06-21 00:05:18 +00:00
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public class ReflectionUtils {
public static <T> T as(Class<T> t, Object o) {
return t.isInstance(o) ? t.cast(o) : null;
}
public static <T extends Enum<?>> T addEnum(Class<T> enumType, String enumName) {
2020-01-26 18:01:16 +00:00
return ReflectionUtils9.addEnum(enumType, enumName);
}
2019-12-21 20:21:25 +00:00
public static void setAccessibleNonFinal(Field field)
2020-01-26 18:01:16 +00:00
throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// let's make the field accessible
field.setAccessible(true);
// next we change the modifier in the Field instance to
// not be final anymore, thus tricking reflection into
// letting us modify the static final field
if (Modifier.isFinal(field.getModifiers())) {
2019-12-21 20:21:25 +00:00
try {
2020-02-22 03:53:25 +00:00
Field lookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
lookupField.setAccessible(true);
2019-12-21 20:21:25 +00:00
// blank out the final bit in the modifiers int
2020-02-22 03:53:25 +00:00
((MethodHandles.Lookup) lookupField.get(null))
.findSetter(Field.class, "modifiers", int.class)
.invokeExact(field, field.getModifiers() & ~Modifier.FINAL);
} catch (Throwable e) {
e.printStackTrace();
2019-12-21 20:21:25 +00:00
}
}
2019-12-21 20:21:25 +00:00
}
public static void setFailsafeFieldValue(Field field, Object target, Object value)
2020-01-31 22:44:50 +00:00
throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
2019-12-21 20:21:25 +00:00
setAccessibleNonFinal(field);
2020-01-26 18:01:16 +00:00
field.set(target,value);
}
private static void blankField(Class<?> enumClass, String fieldName)
2019-12-21 20:21:25 +00:00
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
for (Field field : Class.class.getDeclaredFields()) {
if (field.getName().contains(fieldName)) {
AccessibleObject.setAccessible(new Field[]{field}, true);
setFailsafeFieldValue(field, enumClass, null);
break;
}
}
}
2019-12-21 20:21:25 +00:00
static void cleanEnumCache(Class<?> enumClass)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
blankField(enumClass, "enumConstants"); // IBM JDK
}
private static Class<?> UNMODIFIABLE_MAP = Collections.unmodifiableMap(Collections.emptyMap()).getClass();
public static <T, V> Map<T, V> getMap(Map<T, V> map) {
try {
Class<? extends Map> clazz = map.getClass();
if (clazz != UNMODIFIABLE_MAP) return map;
Field m = clazz.getDeclaredField("m");
m.setAccessible(true);
return (Map<T, V>) m.get(map);
} catch (Throwable e) {
2019-06-27 00:14:00 +00:00
e.printStackTrace();
return map;
}
}
public static <T> List<T> getList(List<T> list) {
try {
2020-01-03 20:38:30 +00:00
Class<? extends List<T>> clazz = (Class<? extends List<T>>) Class.forName("java.util.Collections$UnmodifiableList");
if (!clazz.isInstance(list)) return list;
Field m = clazz.getDeclaredField("list");
m.setAccessible(true);
return (List<T>) m.get(list);
} catch (Throwable e) {
2019-06-27 00:14:00 +00:00
e.printStackTrace();
return list;
}
}
2019-08-15 19:21:24 +00:00
public static Object getHandle(Object wrapper) {
final Method getHandle = makeMethod(wrapper.getClass(), "getHandle");
return callMethod(getHandle, wrapper);
}
//Utils
2019-08-15 19:21:24 +00:00
public static Method makeMethod(Class<?> clazz, String methodName, Class<?>... parameters) {
try {
2019-05-29 03:23:51 +00:00
return clazz.getDeclaredMethod(methodName, parameters);
2019-08-15 19:21:24 +00:00
} catch (NoSuchMethodException ex) {
return null;
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
2019-08-15 19:21:24 +00:00
public static <T> T callMethod(Method method, Object instance, Object... parameters) {
if (method == null) {
throw new RuntimeException("No such method");
}
method.setAccessible(true);
try {
2019-05-29 03:23:51 +00:00
return (T) method.invoke(instance, parameters);
2019-08-15 19:21:24 +00:00
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
2019-08-15 19:21:24 +00:00
public static <T> Constructor<T> makeConstructor(Class<?> clazz, Class<?>... parameterTypes) {
try {
2019-05-29 03:23:51 +00:00
return (Constructor<T>) clazz.getConstructor(parameterTypes);
2019-08-15 19:21:24 +00:00
} catch (NoSuchMethodException ex) {
return null;
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
2019-08-15 19:21:24 +00:00
public static <T> T callConstructor(Constructor<T> constructor, Object... paramaters) {
if (constructor == null) {
throw new RuntimeException("No such constructor");
}
constructor.setAccessible(true);
try {
return constructor.newInstance(paramaters);
2019-08-15 19:21:24 +00:00
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
2019-08-15 19:21:24 +00:00
public static Field makeField(Class<?> clazz, String name) {
try {
return clazz.getDeclaredField(name);
2019-08-15 19:21:24 +00:00
} catch (NoSuchFieldException ex) {
return null;
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
2019-08-15 19:21:24 +00:00
public static Field findField(Class<?> clazz, Class<?> type, int hasMods, int noMods) {
for (Field field : clazz.getDeclaredFields()) {
if (type == null || type.isAssignableFrom(field.getType())) {
int mods = field.getModifiers();
if ((mods & hasMods) == hasMods && (mods & noMods) == 0) {
return setAccessible(field);
}
}
}
return null;
}
2019-08-15 19:21:24 +00:00
public static Field findField(Class<?> clazz, Class<?> type) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == type) {
return setAccessible(field);
}
}
return null;
}
2019-08-15 19:21:24 +00:00
public static Method findMethod(Class<?> clazz, Class<?> returnType, Class... params) {
return findMethod(clazz, 0, returnType, params);
}
2019-08-15 19:21:24 +00:00
public static Method findMethod(Class<?> clazz, int index, int hasMods, int noMods, Class<?> returnType, Class... params) {
outer:
for (Method method : sortMethods(clazz.getDeclaredMethods())) {
if (returnType == null || method.getReturnType() == returnType) {
Class<?>[] mp = method.getParameterTypes();
int mods = method.getModifiers();
if ((mods & hasMods) != hasMods || (mods & noMods) != 0) continue;
if (params == null) {
if (index-- == 0) return setAccessible(method);
else {
continue;
}
}
if (mp.length == params.length) {
for (int i = 0; i < mp.length; i++) {
if (mp[i] != params[i]) continue outer;
}
if (index-- == 0) return setAccessible(method);
else {
continue;
}
}
}
}
return null;
}
public static Method[] sortMethods(Method[] methods) {
Arrays.sort(methods, Comparator.comparing(Method::getName));
return methods;
}
public static Field[] sortFields(Field[] fields) {
Arrays.sort(fields, Comparator.comparing(Field::getName));
return fields;
}
2019-08-15 19:21:24 +00:00
public static Method findMethod(Class<?> clazz, int index, Class<?> returnType, Class... params) {
return findMethod(clazz, index, 0, 0, returnType, params);
}
2019-08-15 19:21:24 +00:00
public static <T extends AccessibleObject> T setAccessible(T ao) {
ao.setAccessible(true);
return ao;
}
@SuppressWarnings("unchecked")
2019-08-15 19:21:24 +00:00
public static <T> T getField(Field field, Object instance) {
if (field == null) {
throw new RuntimeException("No such field");
}
field.setAccessible(true);
try {
return (T) field.get(instance);
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
2019-08-15 19:21:24 +00:00
public static void setField(Field field, Object instance, Object value) {
if (field == null) {
throw new RuntimeException("No such field");
}
field.setAccessible(true);
try {
field.set(instance, value);
2019-08-15 19:21:24 +00:00
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
2019-08-15 19:21:24 +00:00
public static Class<?> getClass(String name) {
try {
return Class.forName(name);
2019-08-15 19:21:24 +00:00
} catch (ClassNotFoundException ex) {
return null;
}
}
2019-08-15 19:21:24 +00:00
public static <T> Class<? extends T> getClass(String name, Class<T> superClass) {
try {
return Class.forName(name).asSubclass(superClass);
} catch (ClassCastException | ClassNotFoundException ex) {
return null;
}
}
/**
* get RefClass object by real class
*
* @param clazz class
* @return RefClass based on passed class
*/
2019-08-15 19:21:24 +00:00
public static RefClass getRefClass(Class clazz) {
return new RefClass(clazz);
}
/**
* RefClass - utility to simplify work with reflections.
*/
public static class RefClass {
private final Class<?> clazz;
2019-08-15 19:21:24 +00:00
private RefClass(Class<?> clazz) {
this.clazz = clazz;
}
/**
* get passed class
*
* @return class
*/
public Class<?> getRealClass() {
return this.clazz;
}
/**
* see {@link Class#isInstance(Object)}
*
* @param object the object to check
* @return true if object is an instance of this class
*/
2019-08-15 19:21:24 +00:00
public boolean isInstance(Object object) {
return this.clazz.isInstance(object);
}
/**
* get existing method by name and types
*
* @param name name
* @param types method parameters. can be Class or RefClass
* @return RefMethod object
* @throws RuntimeException if method not found
*/
2019-08-15 19:21:24 +00:00
public RefMethod getMethod(String name, Object... types) throws NoSuchMethodException {
try {
final Class[] classes = new Class[types.length];
int i = 0;
2019-08-15 19:21:24 +00:00
for (Object e : types) {
if (e instanceof Class) {
classes[i++] = (Class) e;
} else if (e instanceof RefClass) {
classes[i++] = ((RefClass) e).getRealClass();
} else {
classes[i++] = e.getClass();
}
}
try {
return new RefMethod(this.clazz.getMethod(name, classes));
2019-08-15 19:21:24 +00:00
} catch (NoSuchMethodException ignored) {
return new RefMethod(this.clazz.getDeclaredMethod(name, classes));
}
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* get existing constructor by types
*
* @param types parameters. can be Class or RefClass
* @return RefMethod object
* @throws RuntimeException if constructor not found
*/
2019-08-15 19:21:24 +00:00
public RefConstructor getConstructor(Object... types) {
try {
final Class[] classes = new Class[types.length];
int i = 0;
2019-08-15 19:21:24 +00:00
for (Object e : types) {
if (e instanceof Class) {
classes[i++] = (Class) e;
} else if (e instanceof RefClass) {
classes[i++] = ((RefClass) e).getRealClass();
} else {
classes[i++] = e.getClass();
}
}
try {
return new RefConstructor(this.clazz.getConstructor(classes));
2019-08-15 19:21:24 +00:00
} catch (NoSuchMethodException ignored) {
return new RefConstructor(this.clazz.getDeclaredConstructor(classes));
}
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* find method by type parameters
*
* @param types parameters. can be Class or RefClass
* @return RefMethod object
* @throws RuntimeException if method not found
*/
2019-08-15 19:21:24 +00:00
public RefMethod findMethod(Object... types) {
final Class[] classes = new Class[types.length];
int t = 0;
2019-08-15 19:21:24 +00:00
for (Object e : types) {
if (e instanceof Class) {
classes[t++] = (Class) e;
} else if (e instanceof RefClass) {
classes[t++] = ((RefClass) e).getRealClass();
} else {
classes[t++] = e.getClass();
}
}
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
findMethod:
2019-08-15 19:21:24 +00:00
for (Method m : methods) {
final Class<?>[] methodTypes = m.getParameterTypes();
if (methodTypes.length != classes.length) {
continue;
}
2019-08-15 19:21:24 +00:00
for (Class aClass : classes) {
if (!Arrays.equals(classes, methodTypes)) {
continue findMethod;
}
return new RefMethod(m);
}
}
throw new RuntimeException("no such method");
}
/**
* find method by name
*
* @param names possible names of method
* @return RefMethod object
* @throws RuntimeException if method not found
*/
2019-08-15 19:21:24 +00:00
public RefMethod findMethodByName(String... names) {
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
2019-08-15 19:21:24 +00:00
for (Method m : methods) {
for (String name : names) {
if (m.getName().equals(name)) {
return new RefMethod(m);
}
}
}
throw new RuntimeException("no such method");
}
/**
* find method by return value
*
* @param type type of returned value
* @return RefMethod
* @throws RuntimeException if method not found
*/
2019-08-15 19:21:24 +00:00
public RefMethod findMethodByReturnType(RefClass type) {
return this.findMethodByReturnType(type.clazz);
}
/**
* find method by return value
*
* @param type type of returned value
* @return RefMethod
* @throws RuntimeException if method not found
*/
public RefMethod findMethodByReturnType(Class type) {
if (type == null) {
type = void.class;
}
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
2019-08-15 19:21:24 +00:00
for (Method m : methods) {
if (type.equals(m.getReturnType())) {
return new RefMethod(m);
}
}
throw new RuntimeException("no such method");
}
/**
* find constructor by number of arguments
*
* @param number number of arguments
* @return RefConstructor
* @throws RuntimeException if constructor not found
*/
2019-08-15 19:21:24 +00:00
public RefConstructor findConstructor(int number) {
2020-01-03 20:38:30 +00:00
final List<Constructor<?>> constructors = new ArrayList<>();
Collections.addAll(constructors, this.clazz.getConstructors());
Collections.addAll(constructors, this.clazz.getDeclaredConstructors());
2020-01-03 20:38:30 +00:00
for (Constructor<?> m : constructors) {
if (m.getParameterTypes().length == number) {
return new RefConstructor(m);
}
}
throw new RuntimeException("no such constructor");
}
/**
* get field by name
*
* @param name field name
* @return RefField
* @throws RuntimeException if field not found
*/
2019-08-15 19:21:24 +00:00
public RefField getField(String name) {
try {
try {
return new RefField(this.clazz.getField(name));
2019-08-15 19:21:24 +00:00
} catch (NoSuchFieldException ignored) {
return new RefField(this.clazz.getDeclaredField(name));
}
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* find field by type
*
* @param type field type
* @return RefField
* @throws RuntimeException if field not found
*/
2019-08-15 19:21:24 +00:00
public RefField findField(RefClass type) {
return this.findField(type.clazz);
}
/**
* find field by type
*
* @param type field type
* @return RefField
* @throws RuntimeException if field not found
*/
public RefField findField(Class type) {
if (type == null) {
type = void.class;
}
final List<Field> fields = new ArrayList<>();
Collections.addAll(fields, this.clazz.getFields());
Collections.addAll(fields, this.clazz.getDeclaredFields());
2019-08-15 19:21:24 +00:00
for (Field f : fields) {
if (type.equals(f.getType())) {
return new RefField(f);
}
}
throw new RuntimeException("no such field");
}
}
/**
* Method wrapper
*/
public static class RefMethod {
private final Method method;
2019-08-15 19:21:24 +00:00
private RefMethod(Method method) {
this.method = method;
method.setAccessible(true);
}
/**
* @return passed method
*/
public Method getRealMethod() {
return this.method;
}
/**
* @return owner class of method
*/
public RefClass getRefClass() {
return new RefClass(this.method.getDeclaringClass());
}
/**
* @return class of method return type
*/
public RefClass getReturnRefClass() {
return new RefClass(this.method.getReturnType());
}
/**
* apply method to object
*
* @param e object to which the method is applied
* @return RefExecutor with method call(...)
*/
2019-08-15 19:21:24 +00:00
public RefExecutor of(Object e) {
return new RefExecutor(e);
}
/**
* call static method
*
* @param params sent parameters
* @return return value
*/
2019-08-15 19:21:24 +00:00
public Object call(Object... params) {
try {
return this.method.invoke(null, params);
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public class RefExecutor {
final Object e;
2019-08-15 19:21:24 +00:00
public RefExecutor(Object e) {
this.e = e;
}
/**
* apply method for selected object
*
* @param params sent parameters
* @return return value
* @throws RuntimeException if something went wrong
*/
2019-08-15 19:21:24 +00:00
public Object call(Object... params) {
try {
return RefMethod.this.method.invoke(this.e, params);
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
/**
* Constructor wrapper
*/
public static class RefConstructor {
2020-01-31 22:44:50 +00:00
private final Constructor<?> constructor;
2020-01-31 22:44:50 +00:00
private RefConstructor(Constructor<?> constructor) {
this.constructor = constructor;
constructor.setAccessible(true);
}
/**
* @return passed constructor
*/
2020-01-31 22:44:50 +00:00
public Constructor<?> getRealConstructor() {
return this.constructor;
}
/**
* @return owner class of method
*/
public RefClass getRefClass() {
return new RefClass(this.constructor.getDeclaringClass());
}
/**
* create new instance with constructor
*
* @param params parameters for constructor
* @return new object
* @throws RuntimeException if something went wrong
*/
2019-08-15 19:21:24 +00:00
public Object create(Object... params) {
try {
return this.constructor.newInstance(params);
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static class RefField {
private final Field field;
2019-08-15 19:21:24 +00:00
private RefField(Field field) {
this.field = field;
field.setAccessible(true);
}
/**
* @return passed field
*/
public Field getRealField() {
return this.field;
}
/**
* @return owner class of field
*/
public RefClass getRefClass() {
return new RefClass(this.field.getDeclaringClass());
}
/**
* @return type of field
*/
public RefClass getFieldRefClass() {
return new RefClass(this.field.getType());
}
/**
2020-01-31 22:44:50 +00:00
* Apply field for object
*
* @param e applied object
* @return RefExecutor with getter and setter
*/
2019-08-15 19:21:24 +00:00
public RefExecutor of(Object e) {
return new RefExecutor(e);
}
public class RefExecutor {
final Object e;
2019-08-15 19:21:24 +00:00
public RefExecutor(Object e) {
this.e = e;
}
/**
2020-01-31 22:44:50 +00:00
* Set field value for applied object
*
* @param param value
*/
2019-08-15 19:21:24 +00:00
public void set(Object param) {
try {
RefField.this.field.set(this.e, param);
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
2020-01-31 22:44:50 +00:00
* Get field value for the applied object..
*
* @return value of field
*/
public Object get() {
try {
return RefField.this.field.get(this.e);
2019-08-15 19:21:24 +00:00
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}