Progress javadoc, various refactor + new module pandalib-commands

This commit is contained in:
2022-08-03 01:45:18 +02:00
parent 660414424e
commit 3bcd8d315b
78 changed files with 791 additions and 683 deletions

View File

@@ -0,0 +1,16 @@
package fr.pandacube.lib.reflect.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicate a concrete wrapper class that implements the annotated interface or abstract class, in case there is no
* proper wrapper for a provided runtime object.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConcreteWrapper {
Class<? extends ReflectWrapper> value();
}

View File

@@ -0,0 +1,42 @@
package fr.pandacube.lib.reflect.wrapper;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import fr.pandacube.lib.util.MappedListView;
public class ReflectListWrapper<W extends ReflectWrapperI> extends MappedListView<Object, W> implements ReflectWrapperTypedI<List<Object>> {
private final Class<W> expectedWrapperClass;
/* package */ ReflectListWrapper(Class<W> expectedWrapperClass) {
this(ArrayList::new, expectedWrapperClass);
}
/* package */
@SuppressWarnings("unchecked")
ReflectListWrapper(Supplier<List<?>> listCreator, Class<W> expectedWrapperClass) {
this((List<Object>) (listCreator == null ? new ArrayList<>() : listCreator.get()), expectedWrapperClass);
}
/* package */ ReflectListWrapper(List<Object> wrappedList, Class<W> expectedWrapperClass) {
super(wrappedList, el -> ReflectWrapper.wrap(el, expectedWrapperClass), ReflectWrapper::unwrap);
this.expectedWrapperClass = expectedWrapperClass;
}
@SuppressWarnings("unchecked")
@Override
public Class<List<Object>> __getRuntimeClass() {
return (Class<List<Object>>) backend.getClass();
}
@Override
public List<Object> __getRuntimeInstance() {
return backend;
}
@Override
public List<W> subList(int fromIndex, int toIndex) {
return new ReflectListWrapper<>(backend.subList(fromIndex, toIndex), expectedWrapperClass);
}
}

View File

@@ -0,0 +1,115 @@
package fr.pandacube.lib.reflect.wrapper;
import com.google.common.collect.MapMaker;
import fr.pandacube.lib.reflect.ReflectConstructor;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
public abstract class ReflectWrapper implements ReflectWrapperI {
private static final Map<Object, ReflectWrapperI> objectWrapperCache = new MapMaker().weakKeys().makeMap();
public static Object unwrap(ReflectWrapperI wr) {
return wr == null ? null : wr.__getRuntimeInstance();
}
public static <T> T unwrap(ReflectWrapperTypedI<T> wr) {
return wr == null ? null : wr.__getRuntimeInstance();
}
public static <W extends ReflectWrapperI> W wrap(Object runtimeObj) {
return wrap(runtimeObj, null);
}
public static <T, W extends ReflectWrapperTypedI<T>> W wrapTyped(T runtimeObj, Class<W> expectedWrapperClass) {
return wrap(runtimeObj, expectedWrapperClass);
}
public static <W extends ReflectWrapperI> W wrap(Object runtimeObj, Class<W> expectedWrapperClass) {
if (runtimeObj == null)
return null;
synchronized (objectWrapperCache) {
if (objectWrapperCache.containsKey(runtimeObj)) {
ReflectWrapperI wrapper = objectWrapperCache.get(runtimeObj);
if (expectedWrapperClass == null || expectedWrapperClass.isInstance(wrapper)) {
@SuppressWarnings("unchecked")
W wr = (W) wrapper;
return wr;
}
}
Class<?> runtimeClass = runtimeObj.getClass();
Class<?> expectedRuntimeClass = (expectedWrapperClass == null) ? null : WrapperRegistry.getRuntimeClassOfWrapperClass(expectedWrapperClass);
if (expectedRuntimeClass != null && !expectedRuntimeClass.isAssignableFrom(runtimeClass)) {
throw new ClassCastException("Runtime class " + runtimeClass + " is not a sub-class or a sub-interface of expected runtime class " + expectedRuntimeClass + "" +
" (expected wrapper class " + expectedWrapperClass + ").");
}
Class<? extends ReflectWrapperI> wrapperClass = WrapperRegistry.getWrapperOfRuntimeClass(runtimeClass);
if (wrapperClass == null) {
// trying to use the provided expectedWrapperClass
if (expectedWrapperClass == null || expectedRuntimeClass == null) { // implicitly: expectedWrapperClass is null or it has no corresponding runtimeClass
// TODO try to search among all registered wrapper class for one that can support the provided object
throw new IllegalArgumentException("No wrapper available to wrap an instance of runtime class " + runtimeClass + "." +
(expectedWrapperClass != null ? (" Expected wrapper class " + expectedWrapperClass + " is also not valid.") : ""));
}
wrapperClass = expectedWrapperClass;
}
if (expectedWrapperClass != null && !expectedWrapperClass.isAssignableFrom(wrapperClass)) {
throw new ClassCastException("Wrapper class " + wrapperClass + " is not a sub-class or a sub-interface of expected wrapper class" + expectedWrapperClass);
}
ReflectConstructor<? extends ReflectWrapperI> constructor = WrapperRegistry.getWrapperConstructorOfWrapperClass(wrapperClass);
if (constructor == null) {
throw new IllegalStateException("Unable to find a constructor to instanciate " + wrapperClass + " to wrap an instance of " + runtimeObj);
}
ReflectWrapperI wrapper = wrapEx(() -> constructor.instanciate(runtimeObj));
// added to cache by constructor
@SuppressWarnings("unchecked")
W wr = (W) wrapper;
return wr;
}
}
public static <W extends ReflectWrapperI> ReflectListWrapper<W> wrapList(List<Object> runtimeList, Class<W> expectedWrapperClass) {
return new ReflectListWrapper<>(runtimeList, expectedWrapperClass);
}
protected final Object reflectObject;
protected ReflectWrapper(Object obj) {
Objects.requireNonNull(obj);
if (!__getRuntimeClass().isInstance(obj)) {
throw new ClassCastException(obj.getClass() + " object is not instanceof " + __getRuntimeClass());
}
reflectObject = obj;
objectWrapperCache.put(obj, this);
}
@Override
public Object __getRuntimeInstance() {
return reflectObject;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ReflectWrapper wr) {
return Objects.equals(reflectObject, wr.reflectObject);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(reflectObject);
}
}

View File

@@ -0,0 +1,12 @@
package fr.pandacube.lib.reflect.wrapper;
public interface ReflectWrapperI {
default Class<?> __getRuntimeClass() {
return WrapperRegistry.getRuntimeClassOfWrapperClass(getClass());
}
Object __getRuntimeInstance();
}

View File

@@ -0,0 +1,19 @@
package fr.pandacube.lib.reflect.wrapper;
public abstract class ReflectWrapperTyped<T> extends ReflectWrapper implements ReflectWrapperTypedI<T> {
protected ReflectWrapperTyped(Object obj) {
super(obj);
}
@Override
public Class<? extends T> __getRuntimeClass() {
return ReflectWrapperTypedI.super.__getRuntimeClass();
}
@SuppressWarnings("unchecked")
@Override
public T __getRuntimeInstance() {
return (T) super.__getRuntimeInstance();
}
}

View File

@@ -0,0 +1,12 @@
package fr.pandacube.lib.reflect.wrapper;
public interface ReflectWrapperTypedI<T> extends ReflectWrapperI {
@SuppressWarnings("unchecked")
@Override
default Class<? extends T> __getRuntimeClass() {
return (Class<? extends T>) ReflectWrapperI.super.__getRuntimeClass();
}
@Override
T __getRuntimeInstance();
}

View File

@@ -0,0 +1,79 @@
package fr.pandacube.lib.reflect.wrapper;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectConstructor;
import fr.pandacube.lib.util.Log;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class WrapperRegistry {
/* package */ static Class<? extends ReflectWrapperI> getWrapperOfRuntimeClass(Class<?> runtime) {
RegistryEntry e = WRAPPER_DATA_BY_RUNTIME_CLASS.get(runtime);
return e == null ? null : e.wrapperClass;
}
/* package */ static Class<?> getRuntimeClassOfWrapperClass(Class<? extends ReflectWrapperI> wrapperClass) {
RegistryEntry e = WRAPPER_DATA_BY_WRAPPER_CLASS.get(wrapperClass);
return e == null ? null : e.runtimeClass;
}
/* package */ static ReflectConstructor<? extends ReflectWrapperI> getWrapperConstructorOfWrapperClass(Class<? extends ReflectWrapperI> wrapperClass) {
RegistryEntry e = WRAPPER_DATA_BY_WRAPPER_CLASS.get(wrapperClass);
return e == null ? null : e.objectWrapperConstructor;
}
private static final Map<Class<?>, RegistryEntry> WRAPPER_DATA_BY_RUNTIME_CLASS = new HashMap<>();
private static final Map<Class<? extends ReflectWrapperI>, RegistryEntry> WRAPPER_DATA_BY_WRAPPER_CLASS = new HashMap<>();
public static void initWrapper(Class<? extends ReflectWrapperI> wrapper, Class<?> runtime) {
Class<? extends ReflectWrapperI> concreteWrapper = wrapper;
ReflectConstructor<? extends ReflectWrapperI> objectWrapperConstructor;
if (wrapper.isInterface() || Modifier.isAbstract(wrapper.getModifiers())) {
ConcreteWrapper concreteWrapperAnnotation = wrapper.getAnnotation(ConcreteWrapper.class);
if (concreteWrapperAnnotation == null || concreteWrapperAnnotation.value() == null) {
Log.severe("The provided non-concrete (interface or abstract class) wrapper " + wrapper + " does not" +
" provide any concrete wrapper.");
return;
}
concreteWrapper = concreteWrapperAnnotation.value();
if (!wrapper.isAssignableFrom(concreteWrapper)) {
Log.severe("The concrete wrapper " + concreteWrapper + " does not extends or implements " + wrapper + ".");
return;
}
}
try {
objectWrapperConstructor = Reflect.ofClass(concreteWrapper).constructor(Object.class);
} catch (NoSuchMethodException e) {
Log.severe("The wrapper " + concreteWrapper + " does not provide a constructor that takes a unique" +
" Object parameter.", e);
return;
}
RegistryEntry e = new RegistryEntry(runtime, wrapper, concreteWrapper, objectWrapperConstructor);
WRAPPER_DATA_BY_RUNTIME_CLASS.put(runtime, e);
WRAPPER_DATA_BY_WRAPPER_CLASS.put(wrapper, e);
if (concreteWrapper != wrapper) {
WRAPPER_DATA_BY_WRAPPER_CLASS.put(concreteWrapper, e);
}
}
private record RegistryEntry(Class<?> runtimeClass,
Class<? extends ReflectWrapperI> wrapperClass,
Class<? extends ReflectWrapperI> concreteWrapperClass,
ReflectConstructor<? extends ReflectWrapperI> objectWrapperConstructor) {
}
}