[weaver-pojo*] Refactor POJO weaver extensibility

This commit is contained in:
2025-06-21 15:27:15 +02:00
parent 95b2cbc7dd
commit dc318722d3
26 changed files with 414 additions and 203 deletions

View File

@@ -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"))

View File

@@ -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 {
}

View File

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

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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());

View File

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

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
@NullMarked
package de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess;
import org.jspecify.annotations.NullMarked;

View File

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

View File

@@ -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);

View File

@@ -0,0 +1,5 @@
@NullMarked
package de.siphalor.tweed5.weaver.pojo.api.weaving;
import org.jspecify.annotations.NullMarked;

View File

@@ -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 {