[weaver-pojo*] Refactor POJO weaver extensibility
This commit is contained in:
@@ -4,12 +4,14 @@ import de.siphalor.tweed5.annotationinheritance.impl.AnnotationInheritanceResolv
|
|||||||
import de.siphalor.tweed5.typeutils.api.annotations.AnnotationRepeatType;
|
import de.siphalor.tweed5.typeutils.api.annotations.AnnotationRepeatType;
|
||||||
import de.siphalor.tweed5.utils.api.collection.ClassToInstanceMap;
|
import de.siphalor.tweed5.utils.api.collection.ClassToInstanceMap;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.var;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jspecify.annotations.NonNull;
|
import org.jspecify.annotations.NonNull;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AnnotationInheritanceAwareAnnotatedElement implements AnnotatedElement {
|
public class AnnotationInheritanceAwareAnnotatedElement implements AnnotatedElement {
|
||||||
@@ -48,6 +50,28 @@ public class AnnotationInheritanceAwareAnnotatedElement implements AnnotatedElem
|
|||||||
return getOrResolveAnnotations().get(annotationClass);
|
return getOrResolveAnnotations().get(annotationClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull <T extends Annotation> @NotNull T[] getAnnotationsByType(@NonNull Class<T> annotationClass) {
|
||||||
|
T annotation = getOrResolveAnnotations().get(annotationClass);
|
||||||
|
if (annotation != null) {
|
||||||
|
//noinspection unchecked
|
||||||
|
T[] array = (T[]) Array.newInstance(annotationClass, 1);
|
||||||
|
array[0] = annotation;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationRepeatType repeatType = AnnotationRepeatType.getType(annotationClass);
|
||||||
|
if (repeatType instanceof AnnotationRepeatType.Repeatable) {
|
||||||
|
var containerRepeatType = ((AnnotationRepeatType.Repeatable) repeatType).containerRepeatType();
|
||||||
|
Annotation containerAnnotation = getOrResolveAnnotations().get(containerRepeatType.annotationClass());
|
||||||
|
if (containerAnnotation != null) {
|
||||||
|
return containerRepeatType.elements(containerAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T[]) Array.newInstance(annotationClass, 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Annotation[] getAnnotations() {
|
public @NotNull Annotation[] getAnnotations() {
|
||||||
return getOrResolveAnnotations().values().toArray(new Annotation[0]);
|
return getOrResolveAnnotations().values().toArray(new Annotation[0]);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class AnnotationInheritanceResolver {
|
|||||||
public ClassToInstanceMap<Annotation> resolve() {
|
public ClassToInstanceMap<Annotation> resolve() {
|
||||||
resolve(main, Collections.emptySet());
|
resolve(main, Collections.emptySet());
|
||||||
|
|
||||||
ClassToInstanceMap<Annotation> resolvedAnnotations = new ClassToInstanceMap<>();
|
ClassToInstanceMap<Annotation> resolvedAnnotations = ClassToInstanceMap.backedBy(new LinkedHashMap<>());
|
||||||
|
|
||||||
List<Aggregator> aggregatorList = new ArrayList<>(aggregators.values());
|
List<Aggregator> aggregatorList = new ArrayList<>(aggregators.values());
|
||||||
for (int i = aggregatorList.size() - 1; i >= 0; i--) {
|
for (int i = aggregatorList.size() - 1; i >= 0; i--) {
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ class AnnotationInheritanceAwareAnnotatedElementTest {
|
|||||||
.isEqualTo(1);
|
.isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnnotationsByTypeAForTarget1() {
|
||||||
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
|
assertThat(element.getAnnotationsByType(A.class))
|
||||||
|
.singleElement()
|
||||||
|
.extracting(A::value)
|
||||||
|
.isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getAnnotationBForTarget1() {
|
void getAnnotationBForTarget1() {
|
||||||
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
@@ -27,6 +36,15 @@ class AnnotationInheritanceAwareAnnotatedElementTest {
|
|||||||
.isEqualTo(2);
|
.isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnnotationsByTypeBForTarget1() {
|
||||||
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
|
assertThat(element.getAnnotationsByType(B.class))
|
||||||
|
.singleElement()
|
||||||
|
.extracting(B::value)
|
||||||
|
.isEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getAnnotationCForTarget1() {
|
void getAnnotationCForTarget1() {
|
||||||
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
@@ -36,12 +54,33 @@ class AnnotationInheritanceAwareAnnotatedElementTest {
|
|||||||
.isEqualTo(10);
|
.isEqualTo(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnnotationsByTypeCForTarget1() {
|
||||||
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
|
assertThat(element.getAnnotationsByType(C.class))
|
||||||
|
.singleElement()
|
||||||
|
.extracting(C::value)
|
||||||
|
.isEqualTo(10);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getAnnotationRForTarget1() {
|
void getAnnotationRForTarget1() {
|
||||||
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
assertThat(element.getAnnotation(R.class)).isNull();
|
assertThat(element.getAnnotation(R.class)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnnotationsByTypeRForTarget1() {
|
||||||
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
|
assertThat(element.getAnnotationsByType(R.class))
|
||||||
|
.satisfiesExactly(
|
||||||
|
r -> assertThat(r.value()).isEqualTo(4),
|
||||||
|
r -> assertThat(r.value()).isEqualTo(2),
|
||||||
|
r -> assertThat(r.value()).isEqualTo(3),
|
||||||
|
r -> assertThat(r.value()).isEqualTo(10)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getAnnotationRsForTarget1() {
|
void getAnnotationRsForTarget1() {
|
||||||
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
@@ -58,6 +97,21 @@ class AnnotationInheritanceAwareAnnotatedElementTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnnotationsByTypeRsForTarget1() {
|
||||||
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
|
assertThat(element.getAnnotationsByType(Rs.class))
|
||||||
|
.singleElement()
|
||||||
|
.extracting(Rs::value)
|
||||||
|
.asInstanceOf(array(R[].class))
|
||||||
|
.satisfiesExactly(
|
||||||
|
r -> assertThat(r.value()).isEqualTo(4),
|
||||||
|
r -> assertThat(r.value()).isEqualTo(2),
|
||||||
|
r -> assertThat(r.value()).isEqualTo(3),
|
||||||
|
r -> assertThat(r.value()).isEqualTo(10)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getAnnotationsForTarget1() {
|
void getAnnotationsForTarget1() {
|
||||||
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.siphalor.tweed5.patchwork.api;
|
package de.siphalor.tweed5.patchwork.api;
|
||||||
|
|
||||||
import org.jetbrains.annotations.CheckReturnValue;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
@@ -10,7 +9,6 @@ public interface Patchwork {
|
|||||||
@Contract(mutates = "this")
|
@Contract(mutates = "this")
|
||||||
<T extends @Nullable Object> void set(PatchworkPartAccess<T> access, T value) throws InvalidPatchworkAccessException;
|
<T extends @Nullable Object> void set(PatchworkPartAccess<T> access, T value) throws InvalidPatchworkAccessException;
|
||||||
|
|
||||||
@CheckReturnValue
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
Patchwork copy();
|
Patchwork copy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ClassToInstanceMap<T extends @NonNull Object> implements Iterable<T
|
|||||||
return delegate.containsValue(value);
|
return delegate.containsValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V extends T> V get(Class<V> key) {
|
public <V extends T> @Nullable V get(Class<V> key) {
|
||||||
return (V) delegate.get(key);
|
return (V) delegate.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedReaderWriterProvider;
|
import de.siphalor.tweed5.data.extension.api.TweedReaderWriterProvider;
|
||||||
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
||||||
|
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||||
|
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.WeavingContext;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor;
|
|
||||||
import de.siphalor.tweed5.weaver.pojoext.serde.impl.SerdePojoReaderWriterSpec;
|
import de.siphalor.tweed5.weaver.pojoext.serde.impl.SerdePojoReaderWriterSpec;
|
||||||
import lombok.extern.apachecommons.CommonsLog;
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
@@ -18,11 +20,21 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@CommonsLog
|
@CommonsLog
|
||||||
public class ReadWritePojoPostProcessor implements TweedPojoWeavingPostProcessor {
|
public class ReadWritePojoWeavingProcessor implements TweedPojoWeavingExtension {
|
||||||
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryReader<?, ?>>> readerFactories = new HashMap<>();
|
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryReader<?, ?>>> readerFactories = new HashMap<>();
|
||||||
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryWriter<?, ?>>> writerFactories = new HashMap<>();
|
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryWriter<?, ?>>> writerFactories = new HashMap<>();
|
||||||
|
private final ReadWriteExtension readWriteExtension;
|
||||||
|
|
||||||
public ReadWritePojoPostProcessor() {
|
public ReadWritePojoWeavingProcessor(ConfigContainer<?> configContainer) {
|
||||||
|
this.readWriteExtension = configContainer.extension(ReadWriteExtension.class)
|
||||||
|
.orElseThrow(() -> new IllegalStateException(
|
||||||
|
"You must register a " + ReadWriteExtension.class.getSimpleName()
|
||||||
|
+ " to use the " + getClass().getSimpleName()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup(SetupContext context) {
|
||||||
loadProviders();
|
loadProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,18 +75,12 @@ public class ReadWritePojoPostProcessor implements TweedPojoWeavingPostProcessor
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(ConfigEntry<?> configEntry, WeavingContext context) {
|
public <T> void afterWeaveEntry(ActualType<T> valueType, ConfigEntry<T> configEntry, WeavingContext context) {
|
||||||
EntryReadWriteConfig entryConfig = context.annotations().getAnnotation(EntryReadWriteConfig.class);
|
EntryReadWriteConfig entryConfig = context.annotations().getAnnotation(EntryReadWriteConfig.class);
|
||||||
if (entryConfig == null) {
|
if (entryConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadWriteExtension readWriteExtension = context.configContainer().extension(ReadWriteExtension.class).orElse(null);
|
|
||||||
if (readWriteExtension == null && log.isErrorEnabled()) {
|
|
||||||
log.error("You must not use " + getClass().getName() + " without the " + ReadWriteExtension.class.getName());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection rawtypes,unchecked
|
//noinspection rawtypes,unchecked
|
||||||
readWriteExtension.setEntryReaderWriter(
|
readWriteExtension.setEntryReaderWriter(
|
||||||
(ConfigEntry) configEntry,
|
(ConfigEntry) configEntry,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.siphalor.tweed5.weaver.pojoext.serde;
|
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||||
@@ -10,11 +10,12 @@ import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
|||||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
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.PojoWeaving;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension;
|
||||||
import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper;
|
import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper;
|
||||||
import de.siphalor.tweed5.weaver.pojoext.serde.api.EntryReadWriteConfig;
|
|
||||||
import de.siphalor.tweed5.weaver.pojoext.serde.api.ReadWritePojoPostProcessor;
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
@@ -22,12 +23,13 @@ import java.io.StringWriter;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class WeaverPojoSerdeExtensionTest {
|
class ReadWritePojoWeavingProcessorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
void testAnnotated() {
|
void testAnnotated() {
|
||||||
TweedPojoWeaverBootstrapper<AnnotatedConfig> weaverBootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class);
|
TweedPojoWeaverBootstrapper<AnnotatedConfig> weaverBootstrapper = TweedPojoWeaverBootstrapper.create(
|
||||||
|
AnnotatedConfig.class);
|
||||||
|
|
||||||
ConfigContainer<AnnotatedConfig> configContainer = weaverBootstrapper.weave();
|
ConfigContainer<AnnotatedConfig> configContainer = weaverBootstrapper.weave();
|
||||||
configContainer.initialize();
|
configContainer.initialize();
|
||||||
@@ -38,12 +40,27 @@ class WeaverPojoSerdeExtensionTest {
|
|||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
HjsonWriter hjsonWriter = new HjsonWriter(stringWriter, new HjsonWriter.Options());
|
HjsonWriter hjsonWriter = new HjsonWriter(stringWriter, new HjsonWriter.Options());
|
||||||
readWriteExtension.write(hjsonWriter, config, configContainer.rootEntry(), readWriteExtension.createReadWriteContextExtensionsData());
|
readWriteExtension.write(
|
||||||
|
hjsonWriter,
|
||||||
|
config,
|
||||||
|
configContainer.rootEntry(),
|
||||||
|
readWriteExtension.createReadWriteContextExtensionsData()
|
||||||
|
);
|
||||||
|
|
||||||
assertThat(stringWriter).hasToString("{\n\tanInt: 123\n\ttext: test\n\ttest: my cool custom writer\n}\n");
|
assertThat(stringWriter).hasToString("""
|
||||||
|
{
|
||||||
|
\tanInt: 123
|
||||||
|
\ttext: test
|
||||||
|
\ttest: my cool custom writer
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
|
||||||
HjsonReader reader = new HjsonReader(new HjsonLexer(new StringReader(
|
HjsonReader reader = new HjsonReader(new HjsonLexer(new StringReader("""
|
||||||
"{\n\tanInt: 987\n\ttext: abdef\n\ttest: { inner: 29 }\n}"
|
{
|
||||||
|
\tanInt: 987
|
||||||
|
\ttext: abdef
|
||||||
|
\ttest: { inner: 29 }
|
||||||
|
}"""
|
||||||
)));
|
)));
|
||||||
assertThat(readWriteExtension.read(
|
assertThat(readWriteExtension.read(
|
||||||
reader,
|
reader,
|
||||||
@@ -59,10 +76,10 @@ class WeaverPojoSerdeExtensionTest {
|
|||||||
context.registerWriterFactory("tweed5.test.dummy", delegates -> new TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>() {
|
context.registerWriterFactory("tweed5.test.dummy", delegates -> new TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>() {
|
||||||
@Override
|
@Override
|
||||||
public void write(
|
public void write(
|
||||||
@NonNull TweedDataVisitor writer,
|
TweedDataVisitor writer,
|
||||||
Object value,
|
@Nullable Object value,
|
||||||
ConfigEntry<Object> entry,
|
ConfigEntry<Object> entry,
|
||||||
@NonNull TweedWriteContext context
|
TweedWriteContext context
|
||||||
) throws TweedDataWriteException {
|
) throws TweedDataWriteException {
|
||||||
writer.visitString("my cool custom writer");
|
writer.visitString("my cool custom writer");
|
||||||
}
|
}
|
||||||
@@ -70,9 +87,12 @@ class WeaverPojoSerdeExtensionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PojoWeaving(extensions = ReadWriteExtension.class, postProcessors = ReadWritePojoPostProcessor.class)
|
@PojoWeaving(extensions = ReadWriteExtension.class)
|
||||||
|
@DefaultWeavingExtensions
|
||||||
|
@PojoWeavingExtension(de.siphalor.tweed5.weaver.pojoext.serde.api.ReadWritePojoWeavingProcessor.class)
|
||||||
@CompoundWeaving
|
@CompoundWeaving
|
||||||
@EntryReadWriteConfig("tweed5.compound")
|
@EntryReadWriteConfig("tweed5.compound")
|
||||||
|
// lombok
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.siphalor.tweed5.weaver.pojoext.serde.impl;
|
package de.siphalor.tweed5.weaver.pojoext.serde.impl;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.CsvSource;
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
@@ -52,7 +53,7 @@ class SerdePojoReaderWriterSpecTest {
|
|||||||
"abc..def;5;.",
|
"abc..def;5;.",
|
||||||
})
|
})
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
void parseError(String input, int index, String codePoint) {
|
void parseError(String input, int index, @Nullable String codePoint) {
|
||||||
assertThatThrownBy(() -> SerdePojoReaderWriterSpec.parse(input))
|
assertThatThrownBy(() -> SerdePojoReaderWriterSpec.parse(input))
|
||||||
.asInstanceOf(type(SerdePojoReaderWriterSpec.ParseException.class))
|
.asInstanceOf(type(SerdePojoReaderWriterSpec.ParseException.class))
|
||||||
.isInstanceOf(SerdePojoReaderWriterSpec.ParseException.class)
|
.isInstanceOf(SerdePojoReaderWriterSpec.ParseException.class)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":tweed5-construct"))
|
implementation(project(":tweed5-construct"))
|
||||||
|
api(project(":tweed5-annotation-inheritance"))
|
||||||
api(project(":tweed5-core"))
|
api(project(":tweed5-core"))
|
||||||
api(project(":tweed5-naming-format"))
|
api(project(":tweed5-naming-format"))
|
||||||
api(project(":tweed5-type-utils"))
|
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.core.impl.DefaultConfigContainer;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver;
|
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.TrivialPojoWeaver;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver;
|
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -14,16 +13,9 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
public @interface PojoWeaving {
|
public @interface PojoWeaving {
|
||||||
Class<? extends ConfigContainer> container() default DefaultConfigContainer.class;
|
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 {};
|
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.*;
|
||||||
import java.util.function.IntFunction;
|
import java.util.function.IntFunction;
|
||||||
|
|
||||||
public class CollectionPojoWeaver implements TweedPojoWeaver {
|
public class CollectionPojoWeaver implements TweedPojoWeavingExtension {
|
||||||
private static final CollectionWeavingConfig DEFAULT_WEAVING_CONFIG = CollectionWeavingConfigImpl.builder()
|
private static final CollectionWeavingConfig DEFAULT_WEAVING_CONFIG = CollectionWeavingConfigImpl.builder()
|
||||||
.collectionEntryClass(CollectionConfigEntryImpl.class)
|
.collectionEntryClass(CollectionConfigEntryImpl.class)
|
||||||
.build();
|
.build();
|
||||||
@@ -51,10 +51,8 @@ public class CollectionPojoWeaver implements TweedPojoWeaver {
|
|||||||
|
|
||||||
ConfigEntry<?> elementEntry = context.weaveEntry(
|
ConfigEntry<?> elementEntry = context.weaveEntry(
|
||||||
collectionTypeParams.get(0),
|
collectionTypeParams.get(0),
|
||||||
context.subContextBuilder("element")
|
newExtensionsData,
|
||||||
.annotations(collectionTypeParams.get(0))
|
ProtoWeavingContext.subContextFor(context, "element", collectionTypeParams.get(0))
|
||||||
.extensionsData(newExtensionsData)
|
|
||||||
.build()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return WeavableCollectionConfigEntry.FACTORY
|
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.
|
* 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()
|
private static final CompoundWeavingConfig DEFAULT_WEAVING_CONFIG = CompoundWeavingConfigImpl.builder()
|
||||||
.compoundSourceNamingFormat(NamingFormats.camelCase())
|
.compoundSourceNamingFormat(NamingFormats.camelCase())
|
||||||
.compoundTargetNamingFormat(NamingFormats.camelCase())
|
.compoundTargetNamingFormat(NamingFormats.camelCase())
|
||||||
@@ -147,21 +147,28 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
|||||||
private WeavableCompoundConfigEntry.SubEntry weaveCompoundSubEntry(
|
private WeavableCompoundConfigEntry.SubEntry weaveCompoundSubEntry(
|
||||||
PojoClassIntrospector.Property property,
|
PojoClassIntrospector.Property property,
|
||||||
Patchwork newExtensionsData,
|
Patchwork newExtensionsData,
|
||||||
WeavingContext parentContext
|
WeavingContext context
|
||||||
) {
|
) {
|
||||||
assert weavingConfigAccess != null;
|
assert weavingConfigAccess != null;
|
||||||
CompoundWeavingConfig weavingConfig = newExtensionsData.get(weavingConfigAccess);
|
CompoundWeavingConfig weavingConfig = newExtensionsData.get(weavingConfigAccess);
|
||||||
assert weavingConfig != null;
|
assert weavingConfig != null;
|
||||||
|
|
||||||
String name = convertName(property.field().getName(), weavingConfig);
|
String name = convertName(property.field().getName(), weavingConfig);
|
||||||
WeavingContext subContext = createSubContextForProperty(property, name, newExtensionsData, parentContext);
|
|
||||||
|
|
||||||
ConfigEntry<?> subEntry;
|
ConfigEntry<?> subEntry;
|
||||||
if (property.isFinal()) {
|
if (property.isFinal()) {
|
||||||
// TODO
|
// TODO
|
||||||
throw new UnsupportedOperationException("Final config entries are not supported in weaving yet.");
|
throw new UnsupportedOperationException("Final config entries are not supported in weaving yet.");
|
||||||
} else {
|
} 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(
|
return new StaticPojoCompoundConfigEntry.SubEntry(
|
||||||
@@ -193,18 +200,6 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
|||||||
return namingFormat;
|
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) {
|
private AnnotatedElement collectAnnotationsForField(Field field) {
|
||||||
LayeredTypeAnnotations annotations = new LayeredTypeAnnotations();
|
LayeredTypeAnnotations annotations = new LayeredTypeAnnotations();
|
||||||
annotations.appendLayerFrom(TypeAnnotationLayer.TYPE_DECLARATION, field.getType());
|
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.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||||
|
|
||||||
public class TrivialPojoWeaver implements TweedPojoWeaver {
|
public class TrivialPojoWeaver implements TweedPojoWeavingExtension {
|
||||||
@Override
|
@Override
|
||||||
public void setup(SetupContext context) {
|
public void setup(SetupContext context) {
|
||||||
// nothing to set up here
|
// 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.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||||
import lombok.*;
|
import lombok.AccessLevel;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Value;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
public class WeavingContext implements TweedPojoWeavingFunction.NonNull {
|
@Builder
|
||||||
|
public class WeavingContext {
|
||||||
@Nullable WeavingContext parent;
|
@Nullable WeavingContext parent;
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
TweedPojoWeavingFunction.NonNull weavingFunction;
|
WeavingFn weavingFunction;
|
||||||
ConfigContainer<?> configContainer;
|
ConfigContainer<?> configContainer;
|
||||||
String[] path;
|
String[] path;
|
||||||
Patchwork extensionsData;
|
Patchwork extensionsData;
|
||||||
AnnotatedElement annotations;
|
AnnotatedElement annotations;
|
||||||
|
|
||||||
public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer<?> configContainer) {
|
public <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context) {
|
||||||
return new Builder(null, weavingFunction, configContainer, new String[0]);
|
return weavingFunction.weaveEntry(valueType, extensionsData, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer<?> configContainer, String baseName) {
|
public interface WeavingFn {
|
||||||
return new Builder(null, weavingFunction, configContainer, new String[]{ baseName });
|
<T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context);
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
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.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||||
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
||||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
|
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.WeavingContext;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor;
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.apachecommons.CommonsLog;
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -27,36 +30,43 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class TweedPojoWeaverBootstrapper<T> {
|
public class TweedPojoWeaverBootstrapper<T> {
|
||||||
private final Class<T> pojoClass;
|
private final Class<T> pojoClass;
|
||||||
|
private final AnnotatedElement pojoAnnotations;
|
||||||
private final ConfigContainer<T> configContainer;
|
private final ConfigContainer<T> configContainer;
|
||||||
private final Collection<TweedPojoWeaver> weavers;
|
private final Collection<TweedPojoWeavingExtension> weavingExtensions;
|
||||||
private final Collection<TweedPojoWeavingPostProcessor> postProcessors;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private PatchworkFactory contextExtensionsPatchworkFactory;
|
private PatchworkFactory weavingExtensionsPatchworkFactory;
|
||||||
|
|
||||||
public static <T> TweedPojoWeaverBootstrapper<T> create(Class<T> pojoClass) {
|
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
|
//noinspection unchecked
|
||||||
ConfigContainer<T> configContainer = (ConfigContainer<T>) createConfigContainer((Class<? extends ConfigContainer<?>>) rootWeavingConfig.container());
|
ConfigContainer<T>
|
||||||
|
configContainer
|
||||||
Collection<TweedPojoWeaver> weavers = loadWeavers(Arrays.asList(rootWeavingConfig.weavers()));
|
= (ConfigContainer<T>) createConfigContainer((Class<? extends ConfigContainer<?>>) rootWeavingConfig.container());
|
||||||
Collection<TweedPojoWeavingPostProcessor> postProcessors = loadPostProcessors(Arrays.asList(rootWeavingConfig.postProcessors()));
|
|
||||||
|
|
||||||
configContainer.registerExtensions(rootWeavingConfig.extensions());
|
configContainer.registerExtensions(rootWeavingConfig.extensions());
|
||||||
configContainer.finishExtensionSetup();
|
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()
|
return weaverClasses.stream()
|
||||||
.map(weaverClass -> TweedPojoWeaver.FACTORY.construct(weaverClass).finish())
|
.map(weaverClass ->
|
||||||
.collect(Collectors.toList());
|
TweedPojoWeavingExtension.FACTORY.construct(weaverClass)
|
||||||
}
|
.typedArg(ConfigContainer.class, configContainer)
|
||||||
|
.finish()
|
||||||
private static Collection<TweedPojoWeavingPostProcessor> loadPostProcessors(Collection<Class<? extends TweedPojoWeavingPostProcessor>> postProcessorClasses) {
|
)
|
||||||
return postProcessorClasses.stream()
|
|
||||||
.map(postProcessorClass -> TweedPojoWeavingPostProcessor.FACTORY.construct(postProcessorClass).finish())
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,69 +78,107 @@ public class TweedPojoWeaverBootstrapper<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <A extends Annotation> A expectAnnotation(Class<?> clazz, Class<A> annotationClass) {
|
private static <A extends Annotation> A expectAnnotation(AnnotatedElement element, Class<A> annotationClass) {
|
||||||
A annotation = clazz.getAnnotation(annotationClass);
|
A annotation = element.getAnnotation(annotationClass);
|
||||||
if (annotation == null) {
|
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 {
|
} else {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigContainer<T> weave() {
|
public ConfigContainer<T> weave() {
|
||||||
setupWeavers();
|
setupWeavingExtensions();
|
||||||
WeavingContext weavingContext = createWeavingContext();
|
|
||||||
|
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);
|
configContainer.attachTree(rootEntry);
|
||||||
|
|
||||||
return configContainer;
|
return configContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupWeavers() {
|
private void setupWeavingExtensions() {
|
||||||
PatchworkFactory.Builder contextExtensionsPatchworkFactoryBuilder = PatchworkFactory.builder();
|
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);
|
weaver.setup(setupContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
contextExtensionsPatchworkFactory = contextExtensionsPatchworkFactoryBuilder.build();
|
weavingExtensionsPatchworkFactory = weavingExtensionsPatchworkFactoryBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private WeavingContext createWeavingContext() {
|
private <U> ConfigEntry<U> weaveEntry(
|
||||||
try {
|
ActualType<U> dataClass,
|
||||||
assert contextExtensionsPatchworkFactory != null;
|
Patchwork extensionsData,
|
||||||
Patchwork extensionsData = contextExtensionsPatchworkFactory.create();
|
ProtoWeavingContext protoContext
|
||||||
|
) {
|
||||||
|
extensionsData = extensionsData.copy();
|
||||||
|
|
||||||
return WeavingContext.builder(this::weaveEntry, configContainer)
|
runBeforeWeaveHooks(dataClass, extensionsData, protoContext);
|
||||||
|
|
||||||
|
WeavingContext context = WeavingContext.builder()
|
||||||
|
.parent(protoContext.parent())
|
||||||
|
.weavingFunction(this::weaveEntry)
|
||||||
|
.configContainer(configContainer)
|
||||||
|
.path(protoContext.path())
|
||||||
.extensionsData(extensionsData)
|
.extensionsData(extensionsData)
|
||||||
.annotations(pojoClass)
|
.annotations(new AnnotationInheritanceAwareAnnotatedElement(protoContext.annotations()))
|
||||||
.build();
|
.build();
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new PojoWeavingException("Failed to create weaving context's extension data");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <U> ConfigEntry<U> weaveEntry(ActualType<U> dataClass, WeavingContext context) {
|
for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) {
|
||||||
for (TweedPojoWeaver weaver : weavers) {
|
try {
|
||||||
ConfigEntry<U> configEntry = weaver.weaveEntry(dataClass, context);
|
ConfigEntry<U> configEntry = weavingExtension.weaveEntry(dataClass, context);
|
||||||
if (configEntry != null) {
|
if (configEntry != null) {
|
||||||
applyPostProcessors(configEntry, context);
|
runAfterWeaveHooks(dataClass, configEntry, context);
|
||||||
return configEntry;
|
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");
|
throw new PojoWeavingException("Failed to weave " + dataClass + ": No matching weavers found");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyPostProcessors(ConfigEntry<?> configEntry, WeavingContext context) {
|
private <U> void runBeforeWeaveHooks(
|
||||||
for (TweedPojoWeavingPostProcessor postProcessor : postProcessors) {
|
ActualType<U> dataClass,
|
||||||
|
Patchwork extensionsData,
|
||||||
|
ProtoWeavingContext protoContext
|
||||||
|
) {
|
||||||
|
for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) {
|
||||||
try {
|
try {
|
||||||
postProcessor.apply(configEntry, context);
|
weavingExtension.beforeWeaveEntry(dataClass, extensionsData, protoContext);
|
||||||
} catch (Exception e) {
|
} 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.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
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.patchwork.api.PatchworkFactory;
|
||||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jspecify.annotations.NullUnmarked;
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
import org.junit.jupiter.api.Test;
|
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.isCompoundEntryForClassWith;
|
||||||
import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass;
|
import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -28,19 +30,31 @@ class CompoundPojoWeaverTest {
|
|||||||
|
|
||||||
PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build();
|
PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build();
|
||||||
|
|
||||||
WeavingContext weavingContext = WeavingContext.builder(new TweedPojoWeavingFunction.NonNull() {
|
WeavingContext weavingContext = WeavingContext.builder()
|
||||||
|
.weavingFunction(new WeavingContext.WeavingFn() {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context) {
|
public <T> ConfigEntry<T> weaveEntry(
|
||||||
ConfigEntry<T> entry = compoundWeaver.weaveEntry(valueType, context);
|
ActualType<T> valueType,
|
||||||
if (entry != null) {
|
Patchwork extensionsData,
|
||||||
return entry;
|
ProtoWeavingContext protoContext
|
||||||
} else {
|
) {
|
||||||
return new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType());
|
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())
|
.extensionsData(weavingContextExtensionDataFactory.create())
|
||||||
.annotations(Compound.class)
|
.annotations(Compound.class)
|
||||||
|
.path(new String[0])
|
||||||
|
.configContainer(mock(ConfigContainer.class))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ConfigEntry<Compound> resultEntry = compoundWeaver.weaveEntry(ActualType.ofClass(Compound.class), weavingContext);
|
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.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
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.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.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.CollectionPojoWeaver;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver;
|
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ class TweedPojoWeaverBootstrapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PojoWeaving(extensions = {DummyExtension.class})
|
@PojoWeaving(extensions = {DummyExtension.class})
|
||||||
|
@DefaultWeavingExtensions
|
||||||
@CompoundWeaving(namingFormat = "camel_case")
|
@CompoundWeaving(namingFormat = "camel_case")
|
||||||
@Data
|
@Data
|
||||||
public static class MainCompound {
|
public static class MainCompound {
|
||||||
@@ -88,7 +89,9 @@ class TweedPojoWeaverBootstrapperTest {
|
|||||||
boolean somethingElse;
|
boolean somethingElse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PojoWeaving(weavers = {CompoundPojoWeaver.class, CollectionPojoWeaver.class, TrivialPojoWeaver.class})
|
@PojoWeaving
|
||||||
|
@PojoWeavingExtension(CollectionPojoWeaver.class)
|
||||||
|
@DefaultWeavingExtensions
|
||||||
@CompoundWeaving(namingFormat = "camel_case")
|
@CompoundWeaving(namingFormat = "camel_case")
|
||||||
@Data
|
@Data
|
||||||
public static class CompoundWithList {
|
public static class CompoundWithList {
|
||||||
|
|||||||
Reference in New Issue
Block a user