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; } } }