[weaver-pojo*] Refactor POJO weaver extensibility
This commit is contained in:
@@ -4,6 +4,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":tweed5-construct"))
|
||||
api(project(":tweed5-annotation-inheritance"))
|
||||
api(project(":tweed5-core"))
|
||||
api(project(":tweed5-naming-format"))
|
||||
api(project(":tweed5-type-utils"))
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.annotation;
|
||||
|
||||
import de.siphalor.tweed5.annotationinheritance.api.AnnotationInheritance;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@AnnotationInheritance(passOn = PojoWeavingExtension.class)
|
||||
@PojoWeavingExtension(CompoundPojoWeaver.class)
|
||||
@PojoWeavingExtension(TrivialPojoWeaver.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
public @interface DefaultWeavingExtensions {
|
||||
}
|
||||
@@ -5,8 +5,7 @@ import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -14,16 +13,9 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
public @interface PojoWeaving {
|
||||
Class<? extends ConfigContainer> container() default DefaultConfigContainer.class;
|
||||
|
||||
Class<? extends TweedPojoWeaver>[] weavers() default {
|
||||
CompoundPojoWeaver.class,
|
||||
TrivialPojoWeaver.class,
|
||||
};
|
||||
|
||||
Class<? extends TweedPojoWeavingPostProcessor>[] postProcessors() default {};
|
||||
|
||||
Class<? extends TweedExtension>[] extensions() default {};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.annotation;
|
||||
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Repeatable(PojoWeavingExtensions.class)
|
||||
public @interface PojoWeavingExtension {
|
||||
Class<? extends TweedPojoWeavingExtension> value();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
public @interface PojoWeavingExtensions {
|
||||
PojoWeavingExtension[] value();
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.*;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class CollectionPojoWeaver implements TweedPojoWeaver {
|
||||
public class CollectionPojoWeaver implements TweedPojoWeavingExtension {
|
||||
private static final CollectionWeavingConfig DEFAULT_WEAVING_CONFIG = CollectionWeavingConfigImpl.builder()
|
||||
.collectionEntryClass(CollectionConfigEntryImpl.class)
|
||||
.build();
|
||||
@@ -51,10 +51,8 @@ public class CollectionPojoWeaver implements TweedPojoWeaver {
|
||||
|
||||
ConfigEntry<?> elementEntry = context.weaveEntry(
|
||||
collectionTypeParams.get(0),
|
||||
context.subContextBuilder("element")
|
||||
.annotations(collectionTypeParams.get(0))
|
||||
.extensionsData(newExtensionsData)
|
||||
.build()
|
||||
newExtensionsData,
|
||||
ProtoWeavingContext.subContextFor(context, "element", collectionTypeParams.get(0))
|
||||
);
|
||||
|
||||
return WeavableCollectionConfigEntry.FACTORY
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* A weaver that weaves classes with the {@link CompoundWeaving} annotation as compound entries.
|
||||
*/
|
||||
public class CompoundPojoWeaver implements TweedPojoWeaver {
|
||||
public class CompoundPojoWeaver implements TweedPojoWeavingExtension {
|
||||
private static final CompoundWeavingConfig DEFAULT_WEAVING_CONFIG = CompoundWeavingConfigImpl.builder()
|
||||
.compoundSourceNamingFormat(NamingFormats.camelCase())
|
||||
.compoundTargetNamingFormat(NamingFormats.camelCase())
|
||||
@@ -147,21 +147,28 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
||||
private WeavableCompoundConfigEntry.SubEntry weaveCompoundSubEntry(
|
||||
PojoClassIntrospector.Property property,
|
||||
Patchwork newExtensionsData,
|
||||
WeavingContext parentContext
|
||||
WeavingContext context
|
||||
) {
|
||||
assert weavingConfigAccess != null;
|
||||
CompoundWeavingConfig weavingConfig = newExtensionsData.get(weavingConfigAccess);
|
||||
assert weavingConfig != null;
|
||||
|
||||
String name = convertName(property.field().getName(), weavingConfig);
|
||||
WeavingContext subContext = createSubContextForProperty(property, name, newExtensionsData, parentContext);
|
||||
|
||||
ConfigEntry<?> subEntry;
|
||||
if (property.isFinal()) {
|
||||
// TODO
|
||||
throw new UnsupportedOperationException("Final config entries are not supported in weaving yet.");
|
||||
} else {
|
||||
subEntry = subContext.weaveEntry(ActualType.ofUsedType(property.field().getAnnotatedType()), subContext);
|
||||
subEntry = context.weaveEntry(
|
||||
ActualType.ofUsedType(property.field().getAnnotatedType()),
|
||||
newExtensionsData,
|
||||
ProtoWeavingContext.subContextFor(
|
||||
context,
|
||||
property.field().getName(),
|
||||
collectAnnotationsForField(property.field())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new StaticPojoCompoundConfigEntry.SubEntry(
|
||||
@@ -193,18 +200,6 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
||||
return namingFormat;
|
||||
}
|
||||
|
||||
private WeavingContext createSubContextForProperty(
|
||||
PojoClassIntrospector.Property property,
|
||||
String name,
|
||||
Patchwork newExtensionsData,
|
||||
WeavingContext parentContext
|
||||
) {
|
||||
return parentContext.subContextBuilder(name)
|
||||
.annotations(collectAnnotationsForField(property.field()))
|
||||
.extensionsData(newExtensionsData)
|
||||
.build();
|
||||
}
|
||||
|
||||
private AnnotatedElement collectAnnotationsForField(Field field) {
|
||||
LayeredTypeAnnotations annotations = new LayeredTypeAnnotations();
|
||||
annotations.appendLayerFrom(TypeAnnotationLayer.TYPE_DECLARATION, field.getType());
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Value
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ProtoWeavingContext {
|
||||
@Nullable WeavingContext parent;
|
||||
ConfigContainer<?> configContainer;
|
||||
String[] path;
|
||||
AnnotatedElement annotations;
|
||||
|
||||
public static ProtoWeavingContext subContextFor(
|
||||
WeavingContext weavingContext,
|
||||
String subPathName,
|
||||
AnnotatedElement annotations
|
||||
) {
|
||||
String[] path = Arrays.copyOf(weavingContext.path(), weavingContext.path().length + 1, String[].class);
|
||||
path[path.length - 1] = subPathName;
|
||||
return new ProtoWeavingContext(
|
||||
weavingContext,
|
||||
weavingContext.configContainer(),
|
||||
path,
|
||||
annotations
|
||||
);
|
||||
}
|
||||
|
||||
public static ProtoWeavingContext create(ConfigContainer<?> configContainer, AnnotatedElement annotations) {
|
||||
return new ProtoWeavingContext(
|
||||
null,
|
||||
configContainer,
|
||||
new String[0],
|
||||
annotations
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||
|
||||
public class TrivialPojoWeaver implements TweedPojoWeaver {
|
||||
public class TrivialPojoWeaver implements TweedPojoWeavingExtension {
|
||||
@Override
|
||||
public void setup(SetupContext context) {
|
||||
// nothing to set up here
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
public interface TweedPojoWeaver extends TweedPojoWeavingFunction {
|
||||
TweedConstructFactory<TweedPojoWeaver> FACTORY = TweedConstructFactory.builder(TweedPojoWeaver.class).build();
|
||||
|
||||
@ApiStatus.OverrideOnly
|
||||
void setup(SetupContext context);
|
||||
|
||||
interface SetupContext {
|
||||
<E> PatchworkPartAccess<E> registerWeavingContextExtensionData(Class<E> dataClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public interface TweedPojoWeavingExtension extends TweedPojoWeavingFunction {
|
||||
TweedConstructFactory<TweedPojoWeavingExtension> FACTORY =
|
||||
TweedConstructFactory.builder(TweedPojoWeavingExtension.class)
|
||||
.typedArg(ConfigContainer.class)
|
||||
.build();
|
||||
|
||||
@ApiStatus.OverrideOnly
|
||||
void setup(SetupContext context);
|
||||
|
||||
default <T> void beforeWeaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context) {}
|
||||
|
||||
@Override
|
||||
default @Nullable <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default <T> void afterWeaveEntry(ActualType<T> valueType, ConfigEntry<T> configEntry, WeavingContext context) {}
|
||||
|
||||
interface SetupContext {
|
||||
<E> PatchworkPartAccess<E> registerWeavingContextExtensionData(Class<E> dataClass);
|
||||
}
|
||||
}
|
||||
@@ -4,63 +4,30 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||
import lombok.*;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Value
|
||||
public class WeavingContext implements TweedPojoWeavingFunction.NonNull {
|
||||
@Builder
|
||||
public class WeavingContext {
|
||||
@Nullable WeavingContext parent;
|
||||
@Getter(AccessLevel.NONE)
|
||||
TweedPojoWeavingFunction.NonNull weavingFunction;
|
||||
WeavingFn weavingFunction;
|
||||
ConfigContainer<?> configContainer;
|
||||
String[] path;
|
||||
Patchwork extensionsData;
|
||||
AnnotatedElement annotations;
|
||||
|
||||
public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer<?> configContainer) {
|
||||
return new Builder(null, weavingFunction, configContainer, new String[0]);
|
||||
public <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context) {
|
||||
return weavingFunction.weaveEntry(valueType, extensionsData, context);
|
||||
}
|
||||
|
||||
public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer<?> configContainer, String baseName) {
|
||||
return new Builder(null, weavingFunction, configContainer, new String[]{ baseName });
|
||||
}
|
||||
|
||||
public Builder subContextBuilder(String subPathName) {
|
||||
String[] newPath = Arrays.copyOf(path, path.length + 1);
|
||||
newPath[path.length] = subPathName;
|
||||
return new Builder(this, weavingFunction, configContainer, newPath).extensionsData(extensionsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context) {
|
||||
return weavingFunction.weaveEntry(valueType, context);
|
||||
}
|
||||
|
||||
@Accessors(fluent = true, chain = true)
|
||||
@Setter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static class Builder {
|
||||
@Nullable
|
||||
private final WeavingContext parent;
|
||||
private final TweedPojoWeavingFunction.NonNull weavingFunction;
|
||||
private final ConfigContainer<?> configContainer;
|
||||
private final String[] path;
|
||||
private Patchwork extensionsData;
|
||||
private AnnotatedElement annotations;
|
||||
|
||||
public WeavingContext build() {
|
||||
return new WeavingContext(
|
||||
parent,
|
||||
weavingFunction,
|
||||
configContainer,
|
||||
path,
|
||||
extensionsData,
|
||||
annotations
|
||||
);
|
||||
}
|
||||
public interface WeavingFn {
|
||||
<T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -1,20 +1,23 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.impl.weaving;
|
||||
|
||||
import de.siphalor.tweed5.annotationinheritance.api.AnnotationInheritanceAwareAnnotatedElement;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.ProtoWeavingContext;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -27,36 +30,43 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TweedPojoWeaverBootstrapper<T> {
|
||||
private final Class<T> pojoClass;
|
||||
private final AnnotatedElement pojoAnnotations;
|
||||
private final ConfigContainer<T> configContainer;
|
||||
private final Collection<TweedPojoWeaver> weavers;
|
||||
private final Collection<TweedPojoWeavingPostProcessor> postProcessors;
|
||||
private final Collection<TweedPojoWeavingExtension> weavingExtensions;
|
||||
@Nullable
|
||||
private PatchworkFactory contextExtensionsPatchworkFactory;
|
||||
private PatchworkFactory weavingExtensionsPatchworkFactory;
|
||||
|
||||
public static <T> TweedPojoWeaverBootstrapper<T> create(Class<T> pojoClass) {
|
||||
PojoWeaving rootWeavingConfig = expectAnnotation(pojoClass, PojoWeaving.class);
|
||||
AnnotatedElement pojoAnnotations = new AnnotationInheritanceAwareAnnotatedElement(pojoClass);
|
||||
PojoWeaving rootWeavingConfig = expectAnnotation(pojoAnnotations, PojoWeaving.class);
|
||||
PojoWeavingExtension[] extensionAnnotations = pojoAnnotations.getAnnotationsByType(PojoWeavingExtension.class);
|
||||
|
||||
//noinspection unchecked
|
||||
ConfigContainer<T> configContainer = (ConfigContainer<T>) createConfigContainer((Class<? extends ConfigContainer<?>>) rootWeavingConfig.container());
|
||||
|
||||
Collection<TweedPojoWeaver> weavers = loadWeavers(Arrays.asList(rootWeavingConfig.weavers()));
|
||||
Collection<TweedPojoWeavingPostProcessor> postProcessors = loadPostProcessors(Arrays.asList(rootWeavingConfig.postProcessors()));
|
||||
ConfigContainer<T>
|
||||
configContainer
|
||||
= (ConfigContainer<T>) createConfigContainer((Class<? extends ConfigContainer<?>>) rootWeavingConfig.container());
|
||||
|
||||
configContainer.registerExtensions(rootWeavingConfig.extensions());
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
return new TweedPojoWeaverBootstrapper<>(pojoClass, configContainer, weavers, postProcessors);
|
||||
Collection<TweedPojoWeavingExtension> extensions = loadWeavingExtensions(
|
||||
Arrays.stream(extensionAnnotations).map(PojoWeavingExtension::value).collect(Collectors.toList()),
|
||||
configContainer
|
||||
);
|
||||
|
||||
return new TweedPojoWeaverBootstrapper<>(pojoClass, pojoAnnotations, configContainer, extensions);
|
||||
}
|
||||
|
||||
private static Collection<TweedPojoWeaver> loadWeavers(Collection<Class<? extends TweedPojoWeaver>> weaverClasses) {
|
||||
private static Collection<TweedPojoWeavingExtension> loadWeavingExtensions(
|
||||
Collection<Class<? extends TweedPojoWeavingExtension>> weaverClasses,
|
||||
ConfigContainer<?> configContainer
|
||||
) {
|
||||
return weaverClasses.stream()
|
||||
.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 -> TweedPojoWeavingPostProcessor.FACTORY.construct(postProcessorClass).finish())
|
||||
.map(weaverClass ->
|
||||
TweedPojoWeavingExtension.FACTORY.construct(weaverClass)
|
||||
.typedArg(ConfigContainer.class, configContainer)
|
||||
.finish()
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -68,69 +78,107 @@ public class TweedPojoWeaverBootstrapper<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private static <A extends Annotation> A expectAnnotation(Class<?> clazz, Class<A> annotationClass) {
|
||||
A annotation = clazz.getAnnotation(annotationClass);
|
||||
private static <A extends Annotation> A expectAnnotation(AnnotatedElement element, Class<A> annotationClass) {
|
||||
A annotation = element.getAnnotation(annotationClass);
|
||||
if (annotation == null) {
|
||||
throw new PojoWeavingException("Annotation " + annotationClass.getName() + " must be defined on class " + clazz);
|
||||
throw new PojoWeavingException("Annotation "
|
||||
+ annotationClass.getName()
|
||||
+ " must be defined on class "
|
||||
+ element);
|
||||
} else {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigContainer<T> weave() {
|
||||
setupWeavers();
|
||||
WeavingContext weavingContext = createWeavingContext();
|
||||
setupWeavingExtensions();
|
||||
|
||||
assert weavingExtensionsPatchworkFactory != null;
|
||||
|
||||
ConfigEntry<T> rootEntry = this.weaveEntry(
|
||||
ActualType.ofClass(pojoClass),
|
||||
weavingExtensionsPatchworkFactory.create(),
|
||||
ProtoWeavingContext.create(configContainer, pojoAnnotations)
|
||||
);
|
||||
|
||||
ConfigEntry<T> rootEntry = this.weaveEntry(ActualType.ofClass(pojoClass), weavingContext);
|
||||
configContainer.attachTree(rootEntry);
|
||||
|
||||
return configContainer;
|
||||
}
|
||||
|
||||
private void setupWeavers() {
|
||||
PatchworkFactory.Builder contextExtensionsPatchworkFactoryBuilder = PatchworkFactory.builder();
|
||||
private void setupWeavingExtensions() {
|
||||
PatchworkFactory.Builder weavingExtensionsPatchworkFactoryBuilder = PatchworkFactory.builder();
|
||||
|
||||
TweedPojoWeaver.SetupContext setupContext = contextExtensionsPatchworkFactoryBuilder::registerPart;
|
||||
TweedPojoWeavingExtension.SetupContext setupContext = weavingExtensionsPatchworkFactoryBuilder::registerPart;
|
||||
|
||||
for (TweedPojoWeaver weaver : weavers) {
|
||||
for (TweedPojoWeavingExtension weaver : weavingExtensions) {
|
||||
weaver.setup(setupContext);
|
||||
}
|
||||
|
||||
contextExtensionsPatchworkFactory = contextExtensionsPatchworkFactoryBuilder.build();
|
||||
weavingExtensionsPatchworkFactory = weavingExtensionsPatchworkFactoryBuilder.build();
|
||||
}
|
||||
|
||||
private WeavingContext createWeavingContext() {
|
||||
try {
|
||||
assert contextExtensionsPatchworkFactory != null;
|
||||
Patchwork extensionsData = contextExtensionsPatchworkFactory.create();
|
||||
private <U> ConfigEntry<U> weaveEntry(
|
||||
ActualType<U> dataClass,
|
||||
Patchwork extensionsData,
|
||||
ProtoWeavingContext protoContext
|
||||
) {
|
||||
extensionsData = extensionsData.copy();
|
||||
|
||||
return WeavingContext.builder(this::weaveEntry, configContainer)
|
||||
.extensionsData(extensionsData)
|
||||
.annotations(pojoClass)
|
||||
.build();
|
||||
} catch (Throwable e) {
|
||||
throw new PojoWeavingException("Failed to create weaving context's extension data");
|
||||
}
|
||||
}
|
||||
runBeforeWeaveHooks(dataClass, extensionsData, protoContext);
|
||||
|
||||
private <U> ConfigEntry<U> weaveEntry(ActualType<U> dataClass, WeavingContext context) {
|
||||
for (TweedPojoWeaver weaver : weavers) {
|
||||
ConfigEntry<U> configEntry = weaver.weaveEntry(dataClass, context);
|
||||
if (configEntry != null) {
|
||||
applyPostProcessors(configEntry, context);
|
||||
return configEntry;
|
||||
WeavingContext context = WeavingContext.builder()
|
||||
.parent(protoContext.parent())
|
||||
.weavingFunction(this::weaveEntry)
|
||||
.configContainer(configContainer)
|
||||
.path(protoContext.path())
|
||||
.extensionsData(extensionsData)
|
||||
.annotations(new AnnotationInheritanceAwareAnnotatedElement(protoContext.annotations()))
|
||||
.build();
|
||||
|
||||
for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) {
|
||||
try {
|
||||
ConfigEntry<U> configEntry = weavingExtension.weaveEntry(dataClass, context);
|
||||
if (configEntry != null) {
|
||||
runAfterWeaveHooks(dataClass, configEntry, context);
|
||||
return configEntry;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to run Tweed POJO weaver (" + weavingExtension.getClass().getName() + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new PojoWeavingException("Failed to weave " + dataClass + ": No matching weavers found");
|
||||
}
|
||||
|
||||
private void applyPostProcessors(ConfigEntry<?> configEntry, WeavingContext context) {
|
||||
for (TweedPojoWeavingPostProcessor postProcessor : postProcessors) {
|
||||
private <U> void runBeforeWeaveHooks(
|
||||
ActualType<U> dataClass,
|
||||
Patchwork extensionsData,
|
||||
ProtoWeavingContext protoContext
|
||||
) {
|
||||
for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) {
|
||||
try {
|
||||
postProcessor.apply(configEntry, context);
|
||||
weavingExtension.beforeWeaveEntry(dataClass, extensionsData, protoContext);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to apply Tweed POJO weaver post processor", e);
|
||||
log.error(
|
||||
"Failed to apply Tweed POJO weaver before weave hook ("
|
||||
+ weavingExtension.getClass().getName() + ")",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <U> void runAfterWeaveHooks(ActualType<U> dataClass, ConfigEntry<U> configEntry, WeavingContext context) {
|
||||
for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) {
|
||||
try {
|
||||
weavingExtension.afterWeaveEntry(dataClass, configEntry, context);
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Failed to apply Tweed POJO weaver after weave hook ("
|
||||
+ weavingExtension.getClass().getName() + ")",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,15 @@ package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isCompoundEntryForClassWith;
|
||||
import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -28,19 +30,31 @@ class CompoundPojoWeaverTest {
|
||||
|
||||
PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build();
|
||||
|
||||
WeavingContext weavingContext = WeavingContext.builder(new TweedPojoWeavingFunction.NonNull() {
|
||||
WeavingContext weavingContext = WeavingContext.builder()
|
||||
.weavingFunction(new WeavingContext.WeavingFn() {
|
||||
@Override
|
||||
public @NotNull <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context) {
|
||||
ConfigEntry<T> entry = compoundWeaver.weaveEntry(valueType, context);
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
} else {
|
||||
return new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType());
|
||||
}
|
||||
public <T> ConfigEntry<T> weaveEntry(
|
||||
ActualType<T> valueType,
|
||||
Patchwork extensionsData,
|
||||
ProtoWeavingContext protoContext
|
||||
) {
|
||||
WeavingContext context = WeavingContext.builder()
|
||||
.configContainer(protoContext.configContainer())
|
||||
.annotations(valueType)
|
||||
.extensionsData(extensionsData.copy())
|
||||
.path(protoContext.path())
|
||||
.weavingFunction(protoContext.parent()::weaveEntry)
|
||||
.build();
|
||||
return Objects.requireNonNullElseGet(
|
||||
compoundWeaver.weaveEntry(valueType, context),
|
||||
() -> new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType())
|
||||
);
|
||||
}
|
||||
}, mock(ConfigContainer.class))
|
||||
})
|
||||
.extensionsData(weavingContextExtensionDataFactory.create())
|
||||
.annotations(Compound.class)
|
||||
.path(new String[0])
|
||||
.configContainer(mock(ConfigContainer.class))
|
||||
.build();
|
||||
|
||||
ConfigEntry<Compound> resultEntry = compoundWeaver.weaveEntry(ActualType.ofClass(Compound.class), weavingContext);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@NullMarked
|
||||
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -4,10 +4,10 @@ import com.google.auto.service.AutoService;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.DefaultWeavingExtensions;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.CollectionPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver;
|
||||
import lombok.Data;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -65,6 +65,7 @@ class TweedPojoWeaverBootstrapperTest {
|
||||
}
|
||||
|
||||
@PojoWeaving(extensions = {DummyExtension.class})
|
||||
@DefaultWeavingExtensions
|
||||
@CompoundWeaving(namingFormat = "camel_case")
|
||||
@Data
|
||||
public static class MainCompound {
|
||||
@@ -88,7 +89,9 @@ class TweedPojoWeaverBootstrapperTest {
|
||||
boolean somethingElse;
|
||||
}
|
||||
|
||||
@PojoWeaving(weavers = {CompoundPojoWeaver.class, CollectionPojoWeaver.class, TrivialPojoWeaver.class})
|
||||
@PojoWeaving
|
||||
@PojoWeavingExtension(CollectionPojoWeaver.class)
|
||||
@DefaultWeavingExtensions
|
||||
@CompoundWeaving(namingFormat = "camel_case")
|
||||
@Data
|
||||
public static class CompoundWithList {
|
||||
|
||||
Reference in New Issue
Block a user