[construct, core, weaver-pojo] Replace simple class injections with more sophisticated construction

This commit is contained in:
2025-04-23 11:16:15 +02:00
parent de92d6843f
commit 6e79957207
22 changed files with 1163 additions and 223 deletions

View File

@@ -1,11 +1,9 @@
package de.siphalor.tweed5.weaver.pojo.api.entry;
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
import de.siphalor.tweed5.core.api.entry.CollectionConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoWeavingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.function.IntFunction;
@@ -15,26 +13,13 @@ import java.util.function.IntFunction;
* A constructor taking the value {@link Class}
* and a {@link java.util.function.IntFunction} that allows to instantiate the value class with a single capacity argument.
*/
public interface WeavableCollectionConfigEntry<E, T extends Collection<E>>
extends CollectionConfigEntry<E, T> {
static <E, T extends Collection<E>, C extends WeavableCollectionConfigEntry<E, T>> C instantiate(
Class<C> weavableClass, Class<T> valueClass, IntFunction<T> constructor
) throws PojoWeavingException {
try {
Constructor<C> weavableEntryConstructor = weavableClass.getConstructor(Class.class, IntFunction.class);
return weavableEntryConstructor.newInstance(valueClass, constructor);
} catch (NoSuchMethodException e) {
throw new PojoWeavingException(
"Class " + weavableClass.getName() + " must have constructor with value class and value constructor",
e
);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new PojoWeavingException(
"Failed to instantiate class for weavable collection entry " + weavableClass.getName(),
e
);
}
}
public interface WeavableCollectionConfigEntry<E, T extends Collection<E>> extends CollectionConfigEntry<E, T> {
@SuppressWarnings("rawtypes")
TweedConstructFactory<WeavableCollectionConfigEntry> FACTORY =
TweedConstructFactory.builder(WeavableCollectionConfigEntry.class)
.typedArg(Class.class) // value class
.typedArg(IntFunction.class) // value class constructor with capacity
.build();
void elementEntry(ConfigEntry<E> elementEntry);
}

View File

@@ -1,40 +1,27 @@
package de.siphalor.tweed5.weaver.pojo.api.entry;
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoWeavingException;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Supplier;
/**
* {@inheritDoc}
* <br />
* A constructor taking the value {@link Class} and a {@link MethodHandle} that allows to instantiate the value Class with no arguments.
* A constructor taking the value {@link Class} and a {@link Supplier} that allows to instantiate the value Class with no arguments.
*/
public interface WeavableCompoundConfigEntry<T> extends CompoundConfigEntry<T> {
static <T, C extends WeavableCompoundConfigEntry<T>> C instantiate(
Class<C> weavableClass, Class<T> valueClass, MethodHandle constructorHandle
) throws PojoWeavingException {
try {
Constructor<C> weavableEntryConstructor = weavableClass.getConstructor(Class.class, MethodHandle.class);
return weavableEntryConstructor.newInstance(valueClass, constructorHandle);
} catch (NoSuchMethodException e) {
throw new PojoWeavingException(
"Class " + weavableClass.getName() + " must have constructor with value class and value constructor",
e
);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new PojoWeavingException(
"Failed to instantiate class for weavable compound entry " + weavableClass.getName(),
e
);
}
}
@SuppressWarnings("rawtypes")
TweedConstructFactory<WeavableCompoundConfigEntry> FACTORY =
TweedConstructFactory.builder(WeavableCompoundConfigEntry.class)
.typedArg(Class.class) // the value class
.typedArg(Supplier.class) // constructor for the value class
.build();
void registerSubEntry(SubEntry subEntry);

View File

@@ -31,6 +31,7 @@ public class CollectionPojoWeaver implements TweedPojoWeaver {
this.weavingConfigAccess = context.registerWeavingContextExtensionData(CollectionWeavingConfig.class);
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public @Nullable <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context) {
List<ActualType<?>> collectionTypeParams = valueType.getTypesOfSuperArguments(Collection.class);
@@ -44,12 +45,11 @@ public class CollectionPojoWeaver implements TweedPojoWeaver {
IntFunction<Collection<Object>> constructor = getCollectionConstructor(valueType);
//noinspection unchecked,rawtypes
WeavableCollectionConfigEntry configEntry = WeavableCollectionConfigEntry.instantiate(
(Class) weavingConfig.collectionEntryClass(),
(Class) valueType.declaredType(),
constructor
);
WeavableCollectionConfigEntry configEntry = WeavableCollectionConfigEntry.FACTORY
.construct(Objects.requireNonNull(weavingConfig.collectionEntryClass()))
.typedArg(valueType.declaredType())
.typedArg(IntFunction.class, constructor)
.finish();
configEntry.elementEntry(context.weaveEntry(
collectionTypeParams.get(0),
@@ -62,7 +62,7 @@ public class CollectionPojoWeaver implements TweedPojoWeaver {
return configEntry;
} catch (Exception e) {
throw new PojoWeavingException("Exception occurred trying to weave collectoin for class " + valueType, e);
throw new PojoWeavingException("Exception occurred trying to weave collection for class " + valueType, e);
}
}

View File

@@ -22,6 +22,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.function.Supplier;
/**
* A weaver that weaves classes with the {@link CompoundWeaving} annotation as compound entries.
@@ -112,10 +113,17 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
PojoClassIntrospector classIntrospector,
CompoundWeavingConfig weavingConfig
) {
MethodHandle valueConstructor = classIntrospector.noArgsConstructor();
if (valueConstructor == null) {
MethodHandle valueConstructorHandle = classIntrospector.noArgsConstructor();
if (valueConstructorHandle == null) {
throw new PojoWeavingException("Class " + classIntrospector.type().getName() + " must have public no args constructor");
}
Supplier<?> valueConstructor = () -> {
try {
return valueConstructorHandle.invoke();
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
//noinspection rawtypes
Class<? extends WeavableCompoundConfigEntry> annotationEntryClass = weavingConfig.compoundEntryClass();
@@ -125,11 +133,10 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
? annotationEntryClass
: StaticPojoCompoundConfigEntry.class
);
return WeavableCompoundConfigEntry.instantiate(
weavableEntryClass,
(Class<C>) classIntrospector.type(),
valueConstructor
);
return WeavableCompoundConfigEntry.FACTORY.construct(weavableEntryClass)
.typedArg(classIntrospector.type())
.typedArg(Supplier.class, valueConstructor)
.finish();
}
private boolean shouldIncludeCompoundPropertyInWeaving(PojoClassIntrospector.Property property) {

View File

@@ -1,9 +1,11 @@
package de.siphalor.tweed5.weaver.pojo.api.weaving;
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
import org.jetbrains.annotations.ApiStatus;
public interface TweedPojoWeaver extends TweedPojoWeavingFunction {
TweedConstructFactory<TweedPojoWeaver> FACTORY = TweedConstructFactory.builder(TweedPojoWeaver.class).build();
@ApiStatus.OverrideOnly
void setup(SetupContext context);

View File

@@ -1,8 +1,12 @@
package de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess;
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext;
public interface TweedPojoWeavingPostProcessor {
TweedConstructFactory<TweedPojoWeavingPostProcessor> FACTORY =
TweedConstructFactory.builder(TweedPojoWeavingPostProcessor.class).build();
void apply(ConfigEntry<?> configEntry, WeavingContext context);
}
}

View File

@@ -7,17 +7,17 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCompoundConfigEntry;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandle;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> implements WeavableCompoundConfigEntry<T> {
private final MethodHandle noArgsConstructor;
private final Supplier<T> noArgsConstructor;
private final Map<String, SubEntry> subEntries = new LinkedHashMap<>();
private final Map<String, ConfigEntry<?>> subConfigEntries = new LinkedHashMap<>();
public StaticPojoCompoundConfigEntry(@NotNull Class<T> valueClass, @NotNull MethodHandle noArgsConstructor) {
public StaticPojoCompoundConfigEntry(@NotNull Class<T> valueClass, @NotNull Supplier<T> noArgsConstructor) {
super(valueClass);
this.noArgsConstructor = noArgsConstructor;
}
@@ -66,8 +66,7 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
@Override
public T instantiateCompoundValue() {
try {
//noinspection unchecked
return (T) noArgsConstructor.invoke();
return noArgsConstructor.get();
} catch (Throwable e) {
throw new IllegalStateException("Failed to instantiate compound class", e);
}

View File

@@ -19,8 +19,6 @@ import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;
@@ -63,19 +61,19 @@ public class TweedPojoWeaverBootstrapper<T> {
private static Collection<TweedPojoWeaver> loadWeavers(Collection<Class<? extends TweedPojoWeaver>> weaverClasses) {
return weaverClasses.stream()
.map(weaverClass -> checkImplementsAndInstantiate(TweedPojoWeaver.class, weaverClass))
.map(weaverClass -> TweedPojoWeaver.FACTORY.construct(weaverClass).finish())
.collect(Collectors.toList());
}
private static Collection<TweedPojoWeavingPostProcessor> loadPostProcessors(Collection<Class<? extends TweedPojoWeavingPostProcessor>> postProcessorClasses) {
return postProcessorClasses.stream()
.map(postProcessorClass -> checkImplementsAndInstantiate(TweedPojoWeavingPostProcessor.class, postProcessorClass))
.map(postProcessorClass -> TweedPojoWeavingPostProcessor.FACTORY.construct(postProcessorClass).finish())
.collect(Collectors.toList());
}
private static ConfigContainer<?> createConfigContainer(Class<? extends ConfigContainer<?>> containerClass) {
try {
return checkImplementsAndInstantiate(ConfigContainer.class, containerClass);
return ConfigContainer.FACTORY.construct(containerClass).finish();
} catch (Exception e) {
throw new PojoWeavingException("Failed to instantiate config container");
}
@@ -144,23 +142,6 @@ public class TweedPojoWeaverBootstrapper<T> {
}
}
private static <T> T checkImplementsAndInstantiate(Class<T> superClass, Class<? extends T> clazz) {
if (!superClass.isAssignableFrom(clazz)) {
throw new PojoWeavingException("Class " + clazz.getName() + " must extend/implement " + superClass.getName());
}
return instantiate(clazz);
}
private static <T> T instantiate(Class<T> clazz) {
try {
Constructor<T> constructor = clazz.getConstructor();
return constructor.newInstance();
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
IllegalAccessException e) {
throw new PojoWeavingException("Failed to instantiate class " + clazz.getName(), e);
}
}
public ConfigContainer<T> weave() {
setupWeavers();
WeavingContext weavingContext = createWeavingContext();

View File

@@ -121,4 +121,4 @@ class CompoundPojoWeaverTest {
return weavingConfig.compoundEntryClass();
}
}
}
}