>> cachedSubclasses = new AtomicReference<>();
-
- /**
- * Get all subclasses of the current class, using the ClassGraph library.
- *
- * If the returned list is not yet cached, or {@code forceUpdateCache} is true, then the cache is updated before
- * being returned. This may take some time.
- *
- * The ClassGraph library scan all class files in the class path, then loads those which will be returned by
- * this method.
- *
- * @param forceUpdateCache to force the update of the cache, even if it already filled.
- * @return the list of all subclasses found in all loaded class loader.
- * @throws ClassGraphException if any of the worker threads throws an uncaught exception, or the scan was interrupted. (see {@link ClassGraph#scan})
- * @throws IllegalArgumentException f an exception or error was thrown while trying to load any of the classes. (see {@link ClassInfoList#loadClasses()})
- */
- public List> getAllSubclasses(boolean forceUpdateCache) {
- synchronized (cachedSubclasses) {
- if (forceUpdateCache || cachedSubclasses.get() == null) {
- try (ScanResult scanResult = new ClassGraph().enableClassInfo().ignoreClassVisibility().scan()) {
- cachedSubclasses.set(scanResult.getSubclasses(clazz).loadClasses());
- }
- }
- @SuppressWarnings("unchecked")
- List> ret = (List>) (List>) cachedSubclasses.get();
- return ret;
- }
-
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public static abstract class ReflectMember {
- protected final ReflectClass reflectClass;
- protected final ID identifier;
-
- protected final EL cached;
-
- protected ReflectMember(ReflectClass c, ID id, boolean bypassFilter) throws EX {
- reflectClass = c;
- identifier = id;
- cached = (bypassFilter) ? fetchFiltered() : fetch();
- }
-
-
-
- protected EL fetch() throws EX {
-
- // get element in current class
- try {
- EL el = fetchFromClass(reflectClass.clazz);
- setAccessible(el);
- return el;
- } catch (ReflectiveOperationException e1) {
- @SuppressWarnings("unchecked")
- EX ex = (EX) e1;
-
- // get parent class
- Class super T> superClass = reflectClass.clazz.getSuperclass();
- if (superClass == null)
- throw ex;
-
- // get element in parent class (will do recursion)
- try {
- EL el = fetchFromReflectClass(ofClass(superClass));
- setAccessible(el);
- return el;
- } catch (ReflectiveOperationException e2) {
- ex.addSuppressed(e2);
- throw ex;
- }
- }
- }
-
- protected EL fetchFiltered() throws EX {
-
- // get element in current class
- try {
- EL el = fetchFromClass(reflectClass.clazz);
- setAccessible(el);
- return el;
- } catch (ReflectiveOperationException e1) {
- @SuppressWarnings("unchecked")
- EX ex = (EX) e1;
-
- // trying to bypass filtered member
- try {
- @SuppressWarnings("unchecked")
- EL[] elements = (EL[]) Reflect.ofClassOfInstance(reflectClass.clazz)
- .method(internalMethodNameElementArray(), boolean.class)
- .invoke(reflectClass.clazz, false);
- for (EL element : elements) {
- if (isEqualOurElement(element)) {
- // the values in the elements array have to be copied
- // (using special private methods in reflection api) before using it
- Object reflectionFactoryOfClazz = Reflect.ofClassOfInstance(reflectClass.clazz)
- .method("getReflectionFactory")
- .invoke(reflectClass.clazz);
- @SuppressWarnings("unchecked")
- EL copiedElement = (EL) Reflect.ofClassOfInstance(reflectionFactoryOfClazz)
- .method(internalMethodNameCopyElement(), element.getClass())
- .invoke(reflectionFactoryOfClazz, element);
- setAccessible(copiedElement);
- return copiedElement;
- }
- }
- } catch (ReflectiveOperationException e2) {
- ex.addSuppressed(e2);
- }
-
- throw ex;
- }
- }
-
- protected abstract EL fetchFromClass(Class clazz) throws EX;
- protected abstract EL fetchFromReflectClass(ReflectClass> rc) throws EX;
- protected abstract boolean isEqualOurElement(EL el);
- protected abstract String internalMethodNameElementArray();
- protected abstract String internalMethodNameCopyElement();
- protected abstract void setAccessible(EL el);
-
- public EL get() {
- return cached;
- }
-
- public abstract int getModifiers();
-
- }
-
-
-
-
-
-
-
- public static class ReflectField extends ReflectMember {
-
- /* package */ ReflectField(ReflectClass c, String name, boolean bypassFilter) throws NoSuchFieldException {
- super(c, name, bypassFilter);
- }
-
- @Override protected Field fetchFromClass(Class clazz) throws NoSuchFieldException { return clazz.getDeclaredField(identifier); }
- @Override protected Field fetchFromReflectClass(ReflectClass> rc) throws NoSuchFieldException { return rc.field(identifier).get(); }
- @Override protected boolean isEqualOurElement(Field el) { return identifier.equals(el.getName()); }
- @Override protected String internalMethodNameElementArray() { return "getDeclaredFields0"; }
- @Override protected String internalMethodNameCopyElement() { return "copyField"; }
- @Override protected void setAccessible(Field el) { el.setAccessible(true); }
-
-
-
- public Object getValue(Object instance) throws ReflectiveOperationException {
- return get().get(instance);
- }
-
- public Object getStaticValue() throws ReflectiveOperationException {
- return getValue(null);
- }
-
- public void setValue(Object instance, Object value) throws ReflectiveOperationException {
-
- Field f = get();
- if (Modifier.isFinal(f.getModifiers())) {
- // if the field is final, we have to do some unsafe stuff :/
- if (sunMiscUnsafeInstance != null) { // Java >= 16
- // set the value of the field, directly in the memory
- if (Modifier.isStatic(f.getModifiers())) {
- long offset = sunMiscUnsafeInstance.staticFieldOffset(f);
- sunMiscUnsafeInstance.putObject(sunMiscUnsafeInstance.staticFieldBase(f), offset, value);
- } else {
- long offset = sunMiscUnsafeInstance.objectFieldOffset(f);
- sunMiscUnsafeInstance.putObject(instance, offset, value);
- }
- } else { // Java < 16
- // change the modifier in the Field instance so the method #set(instance, value) doesn’t throw an exception
- int modifiers = f.getModifiers();
- if (Modifier.isFinal(modifiers)) {
- ofClass(Field.class).field("modifiers").fetchFiltered().set(f, modifiers & ~Modifier.FINAL);
- }
- f.set(instance, value);
- }
- }
- else { // not final value
- f.set(instance, value);
- }
- }
-
- public void setStaticValue(Object value) throws ReflectiveOperationException {
- setValue(null, value);
- }
-
- @Override
- public int getModifiers() {
- return get().getModifiers();
- }
-
- }
-
-
-
-
-
-
-
- public static class ReflectMethod extends ReflectMember {
-
- /* package */ ReflectMethod(ReflectClass c, MethodIdentifier methodId, boolean bypassFilter) throws NoSuchMethodException {
- super(c, methodId, bypassFilter);
- }
-
- @Override protected Method fetchFromClass(Class clazz) throws NoSuchMethodException { return clazz.getDeclaredMethod(identifier.methodName, identifier.parameters); }
- @Override protected Method fetchFromReflectClass(ReflectClass> rc) throws NoSuchMethodException { return rc.method(identifier, false).get(); }
- @Override protected boolean isEqualOurElement(Method el) { return identifier.methodName.equals(el.getName()) && Arrays.equals(identifier.parameters, el.getParameterTypes()); }
- @Override protected String internalMethodNameElementArray() { return "getDeclaredMethods0"; }
- @Override protected String internalMethodNameCopyElement() { return "copyMethod"; }
- @Override protected void setAccessible(Method el) { el.setAccessible(true); }
-
- public Object invoke(Object instance, Object... values) throws ReflectiveOperationException {
- return get().invoke(instance, values);
- }
-
- public Object invokeStatic(Object... values) throws ReflectiveOperationException {
- return invoke(null, values);
- }
-
- @Override
- public int getModifiers() {
- return get().getModifiers();
- }
-
- }
-
-
-
-
-
-
- public static class ReflectConstructor extends ReflectMember, NoSuchMethodException> {
-
- /* package */ ReflectConstructor(ReflectClass c, ConstructorIdentifier constructorId, boolean bypassFilter) throws NoSuchMethodException {
- super(c, constructorId, bypassFilter);
- }
-
- // Override since we don't want to recursively search for a constructor
- @Override
- protected Constructor fetch() throws NoSuchMethodException {
- Constructor el = fetchFromClass(reflectClass.clazz);
- setAccessible(el);
- return el;
- }
-
- @Override protected Constructor fetchFromClass(Class clazz) throws NoSuchMethodException { return clazz.getDeclaredConstructor(identifier.parameters); }
- @Override protected Constructor fetchFromReflectClass(ReflectClass> rc) { throw new UnsupportedOperationException(); }
- @Override protected boolean isEqualOurElement(Constructor el) { return Arrays.equals(identifier.parameters, el.getParameterTypes()); }
- @Override protected String internalMethodNameElementArray() { return "getDeclaredConstructors0"; }
- @Override protected String internalMethodNameCopyElement() { return "copyConstructor"; }
- @Override protected void setAccessible(Constructor el) { el.setAccessible(true); }
-
- public T instanciate(Object... values) throws ReflectiveOperationException {
- return get().newInstance(values);
- }
-
- @Override
- public int getModifiers() {
- return get().getModifiers();
- }
-
- }
-
-
-
}
diff --git a/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectClass.java b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectClass.java
new file mode 100644
index 0000000..74ed707
--- /dev/null
+++ b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectClass.java
@@ -0,0 +1,185 @@
+package fr.pandacube.lib.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ClassGraphException;
+import io.github.classgraph.ClassInfoList;
+import io.github.classgraph.ScanResult;
+
+/**
+ * Wrapper of a {@link Class} with less verbose access to methods and properties.
+ * @param the type of the wrapped class.
+ */
+public class ReflectClass {
+ private final Class clazz;
+
+ private final Map> methodCache = Collections.synchronizedMap(new HashMap<>());
+ private final Map> constructorCache = Collections.synchronizedMap(new HashMap<>());
+ private final Map> fieldCache = Collections.synchronizedMap(new HashMap<>());
+
+ ReflectClass(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ /**
+ * Returns the class wrapped by this {@link ReflectClass} instance.
+ * @return the class wrapped by this {@link ReflectClass} instance.
+ */
+ public Class get() {
+ return clazz;
+ }
+
+
+
+
+
+
+
+ ReflectMethod method(MethodIdentifier key, boolean bypassFilter) throws NoSuchMethodException {
+ ReflectMethod method = methodCache.get(key);
+ if (method == null) {
+ method = new ReflectMethod<>(this, key, bypassFilter);
+ methodCache.put(key, method);
+ }
+ return method;
+ }
+
+ /**
+ * Provides a {@link ReflectMethod} wrapping the requested {@link Method}.
+ * @param name the method name.
+ * @param paramTypes the types of the method parameters.
+ * @return a {@link ReflectMethod} wrapping the requested {@link Method}.
+ * @throws NoSuchMethodException if the requested method doesn’t exists in the wrapped class.
+ */
+ public ReflectMethod method(String name, Class>... paramTypes) throws NoSuchMethodException {
+ return method(new MethodIdentifier(name, paramTypes), false);
+ }
+
+ /**
+ * Provides a {@link ReflectMethod} wrapping the requested {@link Method}, bypassing some internal filtering in the
+ * {@link Class} implementation.
+ * @param name the method name.
+ * @param paramTypes the types of the method parameters.
+ * @return a {@link ReflectMethod} wrapping the requested {@link Method}.
+ * @throws NoSuchMethodException if the requested method doesn’t exists in the wrapped class.
+ */
+ public ReflectMethod filteredMethod(String name, Class>... paramTypes) throws NoSuchMethodException {
+ return method(new MethodIdentifier(name, paramTypes), true);
+ }
+
+
+
+
+
+
+ private ReflectConstructor constructor(ConstructorIdentifier key, boolean bypassFilter) throws NoSuchMethodException {
+ ReflectConstructor constructor = constructorCache.get(key);
+ if (constructor == null) {
+ constructor = new ReflectConstructor<>(this, key, bypassFilter);
+ constructorCache.put(key, constructor);
+ }
+ return constructor;
+ }
+
+ /**
+ * Provides a {@link ReflectConstructor} wrapping the requested {@link Constructor}.
+ * @param paramTypes the types of the constructor parameters.
+ * @return a {@link ReflectConstructor} wrapping the requested {@link Constructor}.
+ * @throws NoSuchMethodException if the requested constructor doesn’t exists in the wrapped class.
+ */
+ public ReflectConstructor constructor(Class>... paramTypes) throws NoSuchMethodException {
+ return constructor(new ConstructorIdentifier(paramTypes), false);
+ }
+
+ /**
+ * Provides a {@link ReflectConstructor} wrapping the requested {@link Constructor}, bypassing some internal
+ * filtering in the {@link Class} implementation.
+ * @param paramTypes the types of the constructor parameters.
+ * @return a {@link ReflectConstructor} wrapping the requested {@link Constructor}.
+ * @throws NoSuchMethodException if the requested constructor doesn’t exists in the wrapped class.
+ */
+ public ReflectConstructor filteredConstructor(Class>... paramTypes) throws NoSuchMethodException {
+ return constructor(new ConstructorIdentifier(paramTypes), true);
+ }
+
+
+
+
+
+
+ private ReflectField field0(String name, boolean bypassFilter) throws NoSuchFieldException {
+ ReflectField constructor = fieldCache.get(name);
+ if (constructor == null) {
+ constructor = new ReflectField<>(this, name, bypassFilter);
+ fieldCache.put(name, constructor);
+ }
+ return constructor;
+ }
+
+ /**
+ * Provides a {@link ReflectField} wrapping the requested {@link Field}.
+ * @param name the name of the field.
+ * @return a {@link ReflectField} wrapping the requested {@link Field}.
+ * @throws NoSuchFieldException if the requested field doesn’t exists in the wrapped class.
+ */
+ public ReflectField field(String name) throws NoSuchFieldException {
+ return field0(name, false);
+ }
+
+ /**
+ * Provides a {@link ReflectField} wrapping the requested {@link Field}, bypassing some internal filtering in the
+ * {@link Class} implementation.
+ * @param name the name of the field.
+ * @return a {@link ReflectField} wrapping the requested {@link Field}.
+ * @throws NoSuchFieldException if the requested field doesn’t exists in the wrapped class.
+ */
+ public ReflectField filteredField(String name) throws NoSuchFieldException {
+ return field0(name, true);
+ }
+
+
+
+
+
+
+
+
+
+ private final AtomicReference>> cachedSubclasses = new AtomicReference<>();
+
+ /**
+ * Get all subclasses of the current class, using the ClassGraph library.
+ *
+ * If the returned list is not yet cached, or {@code forceUpdateCache} is true, then the cache is updated before
+ * being returned. This may take some time.
+ *
+ * The ClassGraph library scan all class files in the class path, then loads those which will be returned by
+ * this method.
+ *
+ * @param forceUpdateCache to force the update of the cache, even if it already filled.
+ * @return the list of all subclasses found in all loaded class loader.
+ * @throws ClassGraphException if any of the worker threads throws an uncaught exception, or the scan was interrupted. (see {@link ClassGraph#scan})
+ * @throws IllegalArgumentException f an exception or error was thrown while trying to load any of the classes. (see {@link ClassInfoList#loadClasses()})
+ */
+ public List> getAllSubclasses(boolean forceUpdateCache) {
+ synchronized (cachedSubclasses) {
+ if (forceUpdateCache || cachedSubclasses.get() == null) {
+ try (ScanResult scanResult = new ClassGraph().enableClassInfo().ignoreClassVisibility().scan()) {
+ cachedSubclasses.set(scanResult.getSubclasses(clazz).loadClasses());
+ }
+ }
+ @SuppressWarnings("unchecked")
+ List> ret = (List>) (List>) cachedSubclasses.get();
+ return ret;
+ }
+
+ }
+}
diff --git a/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectConstructor.java b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectConstructor.java
new file mode 100644
index 0000000..3e46103
--- /dev/null
+++ b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectConstructor.java
@@ -0,0 +1,68 @@
+package fr.pandacube.lib.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+
+/**
+ * Wrapper for a class {@link Constructor}.
+ * @param the type of the class declaring the wrapped constructor.
+ */
+public final class ReflectConstructor extends ReflectMember, NoSuchMethodException> {
+
+ /* package */ ReflectConstructor(ReflectClass c, ConstructorIdentifier constructorId, boolean bypassFilter) throws NoSuchMethodException {
+ super(c, constructorId, bypassFilter);
+ }
+
+ // Override since we don't want to recursively search for a constructor
+ @Override
+ protected Constructor fetch() throws NoSuchMethodException {
+ Constructor el = fetchFromClass(reflectClass.get());
+ setAccessible(el);
+ return el;
+ }
+
+ @Override
+ protected Constructor fetchFromClass(Class clazz) throws NoSuchMethodException {
+ return clazz.getDeclaredConstructor(identifier.parameters());
+ }
+
+ @Override
+ protected Constructor fetchFromReflectClass(ReflectClass> rc) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean isEqualOurElement(Constructor el) {
+ return Arrays.equals(identifier.parameters(), el.getParameterTypes());
+ }
+
+ @Override
+ protected String internalMethodNameElementArray() {
+ return "getDeclaredConstructors0";
+ }
+
+ @Override
+ protected String internalMethodNameCopyElement() {
+ return "copyConstructor";
+ }
+
+ /**
+ * Invoke this constructor to create a new instance of the declaring class of this constructor.
+ * @param values the parameters used for the constructor call.
+ * @return the newly created instance.
+ * @throws IllegalAccessException if the wrapped Constructor object is enforcing Java language access control and
+ * the underlying constructor is inaccessible. Note that this
+ * {@link ReflectConstructor} automatically sets the {@link Constructor}’s accessible
+ * flag to true.
+ * @throws IllegalArgumentException if there is any problem with the constructor parameters (wrong number, wrong
+ * type, ...).
+ * @throws InstantiationException if the declaring class of this constructor is an abstract class.
+ * @throws InvocationTargetException if the called constructor throws an exception.
+ * @see Constructor#newInstance(Object...)
+ */
+ public T instanciate(Object... values) throws InvocationTargetException, InstantiationException, IllegalAccessException {
+ return get().newInstance(values);
+ }
+
+}
diff --git a/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectField.java b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectField.java
new file mode 100644
index 0000000..2d4eebc
--- /dev/null
+++ b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectField.java
@@ -0,0 +1,153 @@
+package fr.pandacube.lib.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * Wrapper for a class {@link Field}.
+ * @param the type of the class declaring the wrapped field.
+ */
+public final class ReflectField extends ReflectMember {
+
+ private static sun.misc.Unsafe sunMiscUnsafeInstance;
+ private static Field modifiersFieldInFieldClass;
+ static {
+ RuntimeException ex = null;
+ try {
+ sunMiscUnsafeInstance = Runtime.version().feature() >= 16
+ ? (sun.misc.Unsafe) Reflect.ofClass("sun.misc.Unsafe")
+ .field("theUnsafe")
+ .getStaticValue()
+ : null;
+ } catch (Exception e) {
+ ex = new RuntimeException("Cannot access to sun.misc.Unsafe.theUnsafe value.", e);
+ }
+
+ try {
+ modifiersFieldInFieldClass = Reflect.ofClass(Field.class).field("modifiers").fetchFiltered();
+ } catch (Exception e) {
+ RuntimeException newEx = new RuntimeException("Cannot access " + Field.class + ".modifiers field.", e);
+ if (ex != null)
+ newEx.addSuppressed(ex);
+ ex = newEx;
+ }
+
+ if (ex != null)
+ throw ex;
+ }
+
+ /* package */ ReflectField(ReflectClass c, String name, boolean bypassFilter) throws NoSuchFieldException {
+ super(c, name, bypassFilter);
+ }
+
+ @Override
+ /* package */ Field fetchFromClass(Class clazz) throws NoSuchFieldException {
+ return clazz.getDeclaredField(identifier);
+ }
+
+ @Override
+ /* package */ Field fetchFromReflectClass(ReflectClass> rc) throws NoSuchFieldException {
+ return rc.field(identifier).get();
+ }
+
+ @Override
+ /* package */ boolean isEqualOurElement(Field el) {
+ return identifier.equals(el.getName());
+ }
+
+ @Override
+ /* package */ String internalMethodNameElementArray() {
+ return "getDeclaredFields0";
+ }
+
+ @Override
+ /* package */ String internalMethodNameCopyElement() {
+ return "copyField";
+ }
+
+ /**
+ * Returns the value of this field, in the provided instance.
+ * @param instance the instance in which to get the value of the field.
+ * @return the value of this field, in the provided instance.
+ * @throws IllegalAccessException if the wrapped Field object is enforcing Java language access control and the
+ * underlying field is inaccessible. Note that this {@link ReflectField}
+ * automatically sets the {@link Field}’s accessible flag to true.
+ * @throws IllegalArgumentException if the specified instance is not an instance of the class or interface declaring
+ * the wrapped field (or a subclass or implementor thereof).
+ * @throws NullPointerException if the specified instance is null and the field is an instance field.
+ * @see Field#get(Object)
+ */
+ public Object getValue(Object instance) throws IllegalAccessException {
+ return get().get(instance);
+ }
+
+ /**
+ * Returns the value of this static field.
+ * @return the value of this static field.
+ * @throws IllegalAccessException if the wrapped Field object is enforcing Java language access control and the
+ * underlying field is inaccessible. Note that this {@link ReflectField}
+ * automatically sets the {@link Field}’s accessible flag to true.
+ * @throws NullPointerException if the wrapped {@link Field} is actually an instance field. In this case,
+ * {@link #getValue(Object)} should be called instead with a non-null parameter.
+ * @see Field#get(Object)
+ */
+ public Object getStaticValue() throws IllegalAccessException {
+ return getValue(null);
+ }
+
+ /**
+ * Sets the value of this field, in the provided instance.
+ * @param instance the instance in which to set the value of the field.
+ * @param value the new value for this field.
+ * @throws IllegalAccessException if the wrapped Field object is enforcing Java language access control and the
+ * underlying field is inaccessible. Note that this {@link ReflectField}
+ * automatically sets the {@link Field}’s accessible flag to true.
+ * @throws IllegalArgumentException if the specified instance is not an instance of the class or interface declaring
+ * the wrapped field (or a subclass or implementor thereof).
+ * @throws NullPointerException if the specified instance is null and the field is an instance field.
+ * @see Field#set(Object, Object)
+ */
+ public void setValue(Object instance, Object value) throws IllegalAccessException {
+
+ Field f = get();
+ int realModifiers = f.getModifiers();
+ if (Modifier.isFinal(realModifiers)) {
+ // if the field is final, we have to do some unsafe stuff :/
+ if (sunMiscUnsafeInstance != null) { // Java >= 16
+ // set the value of the field, directly in the memory
+ if (Modifier.isStatic(realModifiers)) {
+ long offset = sunMiscUnsafeInstance.staticFieldOffset(f);
+ sunMiscUnsafeInstance.putObject(sunMiscUnsafeInstance.staticFieldBase(f), offset, value);
+ } else {
+ long offset = sunMiscUnsafeInstance.objectFieldOffset(f);
+ sunMiscUnsafeInstance.putObject(instance, offset, value);
+ }
+ } else { // Java < 16
+ // change the modifier in the Field instance so the method #set(instance, value) doesn’t throw an exception
+ try {
+ modifiersFieldInFieldClass.set(f, realModifiers & ~Modifier.FINAL);
+ f.set(instance, value);
+ } finally {
+ modifiersFieldInFieldClass.set(f, realModifiers);
+ }
+ }
+ } else { // not final value
+ f.set(instance, value);
+ }
+ }
+
+ /**
+ * Sets the value of this static field.
+ * @param value the new value for this field.
+ * @throws IllegalAccessException if the wrapped Field object is enforcing Java language access control and the
+ * underlying field is inaccessible. Note that this {@link ReflectField}
+ * automatically sets the {@link Field}’s accessible flag to true.
+ * @throws NullPointerException if the wrapped {@link Field} is actually an instance field. In this case,
+ * {@link #setValue(Object, Object)} should be called instead with a non-null parameter.
+ * @see Field#set(Object, Object)
+ */
+ public void setStaticValue(Object value) throws IllegalAccessException {
+ setValue(null, value);
+ }
+
+}
diff --git a/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectMember.java b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectMember.java
new file mode 100644
index 0000000..bccdefb
--- /dev/null
+++ b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectMember.java
@@ -0,0 +1,132 @@
+package fr.pandacube.lib.reflect;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * Abstract wrapper for a class member (field, method or constructor).
+ * @param the type of the class declaring the wrapped member.
+ * @param type of object uniquely identifying the member into the class.
+ * @param the type of the class member (like {@link Field}, {@link Method} or {@link Constructor})
+ * @param the type of exception thrown by the {@link Class} method responsible to get the member instance (like
+ * {@link NoSuchFieldException} or {@link NoSuchMethodException}).
+ */
+public sealed abstract class ReflectMember
+ permits ReflectField, ReflectMethod, ReflectConstructor {
+
+ /* package */ final ReflectClass reflectClass;
+ /* package */ final ID identifier;
+ private final EL member;
+
+ /* package */ ReflectMember(ReflectClass c, ID id, boolean bypassFilter) throws EX {
+ reflectClass = c;
+ identifier = id;
+ member = (bypassFilter) ? fetchFiltered() : fetch();
+ }
+
+
+ /* package */ EL fetch() throws EX {
+
+ // get element in current class
+ try {
+ EL el = fetchFromClass(reflectClass.get());
+ setAccessible(el);
+ return el;
+ } catch (ReflectiveOperationException e1) {
+ @SuppressWarnings("unchecked")
+ EX ex = (EX) e1;
+
+ // get parent class
+ Class super T> superClass = reflectClass.get().getSuperclass();
+ if (superClass == null)
+ throw ex;
+
+ // get element in parent class (will do recursion)
+ try {
+ EL el = fetchFromReflectClass(Reflect.ofClass(superClass));
+ setAccessible(el);
+ return el;
+ } catch (ReflectiveOperationException e2) {
+ ex.addSuppressed(e2);
+ throw ex;
+ }
+ }
+ }
+
+ /* package */ EL fetchFiltered() throws EX {
+
+ // get element in current class
+ try {
+ EL el = fetchFromClass(reflectClass.get());
+ setAccessible(el);
+ return el;
+ } catch (ReflectiveOperationException e1) {
+ @SuppressWarnings("unchecked")
+ EX ex = (EX) e1;
+
+ // trying to bypass filtered member
+ try {
+ @SuppressWarnings("unchecked")
+ EL[] elements = (EL[]) Reflect.ofClassOfInstance(reflectClass.get())
+ .method(internalMethodNameElementArray(), boolean.class)
+ .invoke(reflectClass.get(), false);
+ for (EL element : elements) {
+ if (isEqualOurElement(element)) {
+ // the values in the elements array have to be copied
+ // (using special private methods in reflection api) before using it
+ Object reflectionFactoryOfClazz = Reflect.ofClassOfInstance(reflectClass.get())
+ .method("getReflectionFactory")
+ .invoke(reflectClass.get());
+ @SuppressWarnings("unchecked")
+ EL copiedElement = (EL) Reflect.ofClassOfInstance(reflectionFactoryOfClazz)
+ .method(internalMethodNameCopyElement(), element.getClass())
+ .invoke(reflectionFactoryOfClazz, element);
+ setAccessible(copiedElement);
+ return copiedElement;
+ }
+ }
+ } catch (ReflectiveOperationException e2) {
+ ex.addSuppressed(e2);
+ }
+
+ throw ex;
+ }
+ }
+
+ /* package */ abstract EL fetchFromClass(Class clazz) throws EX;
+
+ /* package */ abstract EL fetchFromReflectClass(ReflectClass> rc) throws EX;
+
+ /* package */ abstract boolean isEqualOurElement(EL el);
+
+ /* package */ abstract String internalMethodNameElementArray();
+
+ /* package */ abstract String internalMethodNameCopyElement();
+
+ /* package */ void setAccessible(EL el) {
+ el.setAccessible(true);
+ }
+
+ /**
+ * Returns the wrapped class member.
+ * @return the wrapped class member.
+ */
+ public EL get() {
+ return member;
+ }
+
+ /**
+ * Returns the modifiers of the wrapped class member.
+ * @return the modifiers of the wrapped class member.
+ * @see Field#getModifiers()
+ * @see Method#getModifiers()
+ * @see Constructor#getModifiers()
+ */
+ public int getModifiers() {
+ return get().getModifiers();
+ }
+
+}
diff --git a/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectMethod.java b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectMethod.java
new file mode 100644
index 0000000..37ee479
--- /dev/null
+++ b/pandalib-reflect/src/main/java/fr/pandacube/lib/reflect/ReflectMethod.java
@@ -0,0 +1,80 @@
+package fr.pandacube.lib.reflect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * Wrapper for a class {@link Method}.
+ * @param the type of the class declaring the wrapped method.
+ */
+public final class ReflectMethod extends ReflectMember {
+
+ /* package */ ReflectMethod(ReflectClass c, MethodIdentifier methodId, boolean bypassFilter) throws NoSuchMethodException {
+ super(c, methodId, bypassFilter);
+ }
+
+ @Override
+ protected Method fetchFromClass(Class clazz) throws NoSuchMethodException {
+ return clazz.getDeclaredMethod(identifier.methodName(), identifier.parameters());
+ }
+
+ @Override
+ protected Method fetchFromReflectClass(ReflectClass> rc) throws NoSuchMethodException {
+ return rc.method(identifier, false).get();
+ }
+
+ @Override
+ protected boolean isEqualOurElement(Method el) {
+ return identifier.methodName().equals(el.getName()) && Arrays.equals(identifier.parameters(), el.getParameterTypes());
+ }
+
+ @Override
+ protected String internalMethodNameElementArray() {
+ return "getDeclaredMethods0";
+ }
+
+ @Override
+ protected String internalMethodNameCopyElement() {
+ return "copyMethod";
+ }
+
+ /**
+ * Invokes this method on the specified instance with the specified parameters.
+ * @param instance the instance on which to call the method.
+ * @param values the parameters used for the method call.
+ * @return the eventual return value of the method call.
+ * @throws IllegalAccessException if the wrapped Method object is enforcing Java language access control and the
+ * underlying method is inaccessible. Note that this {@link ReflectMethod}
+ * automatically sets the {@link Method}’s accessible flag to true.
+ * @throws IllegalArgumentException if the specified instance is not an instance of the class or interface declaring
+ * the wrapped method (or a subclass or implementor thereof), or if there is any
+ * problem with the method parameters (wrong number, wrong type, ...).
+ * @throws NullPointerException if the specified instance is null and the field is an instance field.
+ * @throws InvocationTargetException if the called method throws an exception.
+ * @see Method#invoke(Object, Object...)
+ */
+ public Object invoke(Object instance, Object... values) throws InvocationTargetException, IllegalAccessException {
+ return get().invoke(instance, values);
+ }
+
+ /**
+ * Invokes this static method with the specified parameters.
+ * @param values the parameters used for the method call.
+ * @return the eventual return value of the method call.
+ * @throws IllegalAccessException if the wrapped Method object is enforcing Java language access control and the
+ * underlying method is inaccessible. Note that this {@link ReflectMethod}
+ * automatically sets the {@link Method}’s accessible flag to true.
+ * @throws IllegalArgumentException if there is any problem with the method parameters (wrong number, wrong type,
+ * ...).
+ * @throws NullPointerException if the wrapped {@link Method} is actually an instance method. In this case,
+ * {@link #invoke(Object, Object...)} should be called instead with a non-null first
+ * parameter.
+ * @throws InvocationTargetException if the called method throws an exception.
+ * @see Method#invoke(Object, Object...)
+ */
+ public Object invokeStatic(Object... values) throws InvocationTargetException, IllegalAccessException {
+ return invoke(null, values);
+ }
+
+}