From b7b89f3ea915509c7675f605edbac2ce3615ef62 Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Sun, 30 Jan 2022 23:07:51 +0100 Subject: [PATCH] Reflect: classes are now generic and added Constructor reflection --- .../fr/pandacube/lib/core/util/Reflect.java | 148 +++++++++++++----- 1 file changed, 110 insertions(+), 38 deletions(-) diff --git a/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java b/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java index 947cb9a..5ddf165 100644 --- a/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java +++ b/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java @@ -1,5 +1,6 @@ package fr.pandacube.lib.core.util; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -43,17 +44,18 @@ public class Reflect { - private static final Map, ReflectClass> classCache; + private static final Map, ReflectClass> classCache; - public static ReflectClass ofClass(Class clazz) { - return classCache.computeIfAbsent(clazz, ReflectClass::new); + @SuppressWarnings("unchecked") + public static ReflectClass ofClass(Class clazz) { + return (ReflectClass) classCache.computeIfAbsent(clazz, ReflectClass::new); } - public static ReflectClass ofClass(String className) throws ClassNotFoundException { + public static ReflectClass ofClass(String className) throws ClassNotFoundException { return ofClass(Class.forName(className)); } - public static ReflectClass ofClassOfInstance(Object instance) { + public static ReflectClass ofClassOfInstance(Object instance) { if (instance == null) throw new IllegalArgumentException("instance can't be null"); return ofClass(instance.getClass()); @@ -83,31 +85,55 @@ public class Reflect { } } + private record ConstructorIdentifier(Class[] parameters) { + private ConstructorIdentifier { + parameters = (parameters == null) ? new Class[0] : parameters; + } + @Override + public boolean equals(Object other) { + return other != null && other instanceof ConstructorIdentifier o + && Arrays.equals(o.parameters, parameters); + } + @Override + public int hashCode() { + return Arrays.hashCode(parameters); + } + } - public static class ReflectClass { - private Class clazz; - - private final Map methodCache = Collections.synchronizedMap(new HashMap<>()); - private final Map fieldCache = Collections.synchronizedMap(new HashMap<>()); + + public static class ReflectClass { + private 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<>()); - private ReflectClass(Class clazz) { + private ReflectClass(Class clazz) { this.clazz = clazz; } - private ReflectMethod method(MethodIdentifier key) { - return methodCache.computeIfAbsent(key, k -> new ReflectMethod(this, k)); + private ReflectMethod method(MethodIdentifier key) { + return methodCache.computeIfAbsent(key, k -> new ReflectMethod<>(this, k)); } - public ReflectMethod method(String name, Class... paramTypes) { + public ReflectMethod method(String name, Class... paramTypes) { return method(new MethodIdentifier(name, paramTypes)); } - public ReflectField field(String name) { - return fieldCache.computeIfAbsent(name, n -> new ReflectField(this, n)); + private ReflectConstructor constructor(ConstructorIdentifier key) { + return constructorCache.computeIfAbsent(key, k -> new ReflectConstructor<>(this, k)); + } + + public ReflectConstructor constructor(Class... paramTypes) { + return constructor(new ConstructorIdentifier(paramTypes)); + } + + public ReflectField field(String name) { + return fieldCache.computeIfAbsent(name, n -> new ReflectField<>(this, n)); } } @@ -133,23 +159,23 @@ public class Reflect { - public static abstract class ReflectClassEl { - ReflectClass reflectClass; - String elementName; + public static abstract class ReflectClassEl { + ReflectClass reflectClass; - protected ReflectClassEl(ReflectClass c, String elementName) { + protected ReflectClassEl(ReflectClass c) { reflectClass = c; - this.elementName = elementName; } } - public static class ReflectField extends ReflectClassEl { + public static class ReflectField extends ReflectClassEl { + String elementName; private Field cached, cachedFiltered; - /* package */ ReflectField(ReflectClass c, String name) { - super(c, name); + /* package */ ReflectField(ReflectClass c, String name) { + super(c); + elementName = name; } public synchronized Field get() throws NoSuchFieldException { @@ -217,15 +243,17 @@ public class Reflect { } - public static class ReflectMethod extends ReflectClassEl { + public static class ReflectMethod extends ReflectClassEl { + String elementName; private Method cached, cachedFiltered; MethodIdentifier methodId; Class[] parameterTypes; - /* package */ ReflectMethod(ReflectClass c, MethodIdentifier methodId) { - super(c, methodId.methodName); + /* package */ ReflectMethod(ReflectClass c, MethodIdentifier methodId) { + super(c); + this.elementName = methodId.methodName; this.methodId = methodId; parameterTypes = methodId.parameters; } @@ -261,6 +289,46 @@ public class Reflect { } } + public static class ReflectConstructor extends ReflectClassEl { + + private Constructor cached, cachedFiltered; + + ConstructorIdentifier constructorId; + Class[] parameterTypes; + + /* package */ ReflectConstructor(ReflectClass c, ConstructorIdentifier constructorId) { + super(c); + this.constructorId = constructorId; + parameterTypes = constructorId.parameters; + } + + + public Constructor get() throws NoSuchMethodException { + if (cached == null) { + Constructor el = null; + el = reflectClass.clazz.getDeclaredConstructor(parameterTypes); + el.setAccessible(true); + cached = el; + } + return cached; + } + + /* package */ Constructor getFiltered() throws NoSuchMethodException { + if (cachedFiltered == null) { + cachedFiltered = Reflect.getFiltered(reflectClass.clazz, + c -> c.getDeclaredConstructor(parameterTypes), + m -> m.setAccessible(true), + m -> Arrays.equals(parameterTypes, m.getParameterTypes()), + "getDeclaredConstructors0", "copyConstructor"); + } + return cachedFiltered; + } + + public T instanciate(Object... values) throws ReflectiveOperationException { + return get().newInstance(values); + } + } + @@ -272,13 +340,17 @@ public class Reflect { - private interface GetReflectiveElement { + private interface GetUncheckedClassReflectiveElement { public T get(Class clazz) throws E; } - private static T getDeclaredRecursively( - Class clazz, GetReflectiveElement jlrGetter, - GetReflectiveElement parentGetter, Consumer setAccessible) throws E { + private interface GetReflectiveElement { + public T get(Class clazz) throws E; + } + + private static T getDeclaredRecursively( + Class clazz, GetReflectiveElement jlrGetter, + GetUncheckedClassReflectiveElement parentGetter, Consumer setAccessible) throws E { Objects.requireNonNull(clazz, "Class instance not provided"); E ex = null; @@ -289,11 +361,9 @@ public class Reflect { el = jlrGetter.get(clazz); setAccessible.accept(el); } catch (ReflectiveOperationException e) { - if (ex == null) { - @SuppressWarnings("unchecked") - E ee = (E) e; - ex = ee; - } + @SuppressWarnings("unchecked") + E ee = (E) e; + ex = ee; } // get element in parent class (will do recursion) @@ -318,6 +388,8 @@ public class Reflect { return el; } + + /** * Get a Field or Method of a class that is not accessible using {@link Class#getDeclaredField(String)} * or using {@link Class#getDeclaredMethod(String, Class...)} because the implementation of {@link Class} @@ -326,8 +398,8 @@ public class Reflect { * This method calls an internal method of {@link Class} to retrieve the full list of field or method, then * search in the list for the requested element. */ - private static T getFiltered( - Class clazz, GetReflectiveElement jlrGetter, + private static T getFiltered( + Class clazz, GetReflectiveElement jlrGetter, Consumer setAccessible, Predicate elementChecker, String privateMethodName, String copyMethodName) throws E { Objects.requireNonNull(clazz, "Class instance not provided");