[weaver-pojo] Introduce pojo weaver post processors
This commit is contained in:
@@ -9,7 +9,7 @@ import de.siphalor.tweed5.patchwork.api.PatchworkClassCreator;
|
|||||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClass;
|
import de.siphalor.tweed5.patchwork.impl.PatchworkClass;
|
||||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassGenerator;
|
import de.siphalor.tweed5.patchwork.impl.PatchworkClassGenerator;
|
||||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassPart;
|
import de.siphalor.tweed5.patchwork.impl.PatchworkClassPart;
|
||||||
import de.siphalor.tweed5.utils.api.collection.ClassToInstanceMap;
|
import de.siphalor.tweed5.utils.api.collection.InheritanceMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -20,14 +20,18 @@ import java.util.*;
|
|||||||
public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||||
@Getter
|
@Getter
|
||||||
private ConfigContainerSetupPhase setupPhase = ConfigContainerSetupPhase.EXTENSIONS_SETUP;
|
private ConfigContainerSetupPhase setupPhase = ConfigContainerSetupPhase.EXTENSIONS_SETUP;
|
||||||
private final ClassToInstanceMap<TweedExtension> extensions = new ClassToInstanceMap<>();
|
private final InheritanceMap<TweedExtension> extensions = new InheritanceMap<>(TweedExtension.class);
|
||||||
private ConfigEntry<T> rootEntry;
|
private ConfigEntry<T> rootEntry;
|
||||||
private PatchworkClass<EntryExtensionsData> entryExtensionsDataPatchworkClass;
|
private PatchworkClass<EntryExtensionsData> entryExtensionsDataPatchworkClass;
|
||||||
private Map<Class<?>, RegisteredExtensionDataImpl<EntryExtensionsData, ?>> registeredEntryDataExtensions;
|
private Map<Class<?>, RegisteredExtensionDataImpl<EntryExtensionsData, ?>> registeredEntryDataExtensions;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends TweedExtension> @Nullable E extension(Class<E> extensionClass) {
|
public <E extends TweedExtension> @Nullable E extension(Class<E> extensionClass) {
|
||||||
return extensions.get(extensionClass);
|
try {
|
||||||
|
return extensions.getSingleInstance(extensionClass);
|
||||||
|
} catch (InheritanceMap.NonUniqueResultException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -39,8 +43,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
|||||||
public void registerExtension(TweedExtension extension) {
|
public void registerExtension(TweedExtension extension) {
|
||||||
requireSetupPhase(ConfigContainerSetupPhase.EXTENSIONS_SETUP);
|
requireSetupPhase(ConfigContainerSetupPhase.EXTENSIONS_SETUP);
|
||||||
|
|
||||||
TweedExtension previous = extensions.put(extension);
|
if (!extensions.putIfAbsent(extension)) {
|
||||||
if (previous != null) {
|
|
||||||
throw new IllegalArgumentException("Extension " + extension.getClass().getName() + " is already registered");
|
throw new IllegalArgumentException("Extension " + extension.getClass().getName() + " is already registered");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerExtension(TweedExtension extension) {
|
public void registerExtension(TweedExtension extension) {
|
||||||
if (!extensions.containsClass(extension.getClass())) {
|
if (!extensions.containsAnyInstanceForClass(extension.getClass())) {
|
||||||
additionalExtensions.add(extension);
|
additionalExtensions.add(extension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +85,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (TweedExtension additionalExtension : additionalExtensions) {
|
for (TweedExtension additionalExtension : additionalExtensions) {
|
||||||
extensions.put(additionalExtension);
|
extensions.putIfAbsent(additionalExtension);
|
||||||
}
|
}
|
||||||
extensionsToSetup = new ArrayList<>(additionalExtensions);
|
extensionsToSetup = new ArrayList<>(additionalExtensions);
|
||||||
additionalExtensions.clear();
|
additionalExtensions.clear();
|
||||||
|
|||||||
@@ -119,12 +119,12 @@ class ValidationFallbackExtensionImplTest {
|
|||||||
readerWriterData.set(intEntry.extensionsData(), new EntryReaderWriterDefinition() {
|
readerWriterData.set(intEntry.extensionsData(), new EntryReaderWriterDefinition() {
|
||||||
@Override
|
@Override
|
||||||
public TweedEntryReader<?, ?> reader() {
|
public TweedEntryReader<?, ?> reader() {
|
||||||
return TweedEntryReaderWriters.nullableReaderWriter(TweedEntryReaderWriters.intReaderWriter());
|
return TweedEntryReaderWriters.nullableReader(TweedEntryReaderWriters.intReaderWriter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TweedEntryWriter<?, ?> writer() {
|
public TweedEntryWriter<?, ?> writer() {
|
||||||
return TweedEntryReaderWriters.nullableReaderWriter(TweedEntryReaderWriters.intReaderWriter());
|
return TweedEntryReaderWriters.nullableWriter(TweedEntryReaderWriters.intReaderWriter());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
|||||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||||
|
|
||||||
public interface ReadWriteExtension extends TweedExtension {
|
public interface ReadWriteExtension extends TweedExtension {
|
||||||
|
void setEntryReaderWriterDefinition(ConfigEntry<?> entry, EntryReaderWriterDefinition readerWriterDefinition);
|
||||||
|
|
||||||
ReadWriteContextExtensionsData createReadWriteContextExtensionsData();
|
ReadWriteContextExtensionsData createReadWriteContextExtensionsData();
|
||||||
|
|
||||||
<T> T read(TweedDataReader reader, ConfigEntry<T> entry, ReadWriteContextExtensionsData contextExtensionsData) throws TweedEntryReadException;
|
<T> T read(TweedDataReader reader, ConfigEntry<T> entry, ReadWriteContextExtensionsData contextExtensionsData) throws TweedEntryReadException;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.siphalor.tweed5.data.extension.api;
|
package de.siphalor.tweed5.data.extension.api;
|
||||||
|
|
||||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,14 +12,11 @@ public interface TweedReaderWriterProvider {
|
|||||||
/**
|
/**
|
||||||
* The context where reader and writer factories may be registered.<br />
|
* The context where reader and writer factories may be registered.<br />
|
||||||
* The reader and writer ids must be globally unique. It is therefore recommended to scope custom reader and writer ids in your own namespace (e.g. "de.siphalor.custom.blub")
|
* The reader and writer ids must be globally unique. It is therefore recommended to scope custom reader and writer ids in your own namespace (e.g. "de.siphalor.custom.blub")
|
||||||
|
* Ids may consist of alphanumeric characters and dots.
|
||||||
*/
|
*/
|
||||||
interface ProviderContext {
|
interface ProviderContext {
|
||||||
void registerReaderFactory(String id, ReaderWriterFactory<? extends TweedEntryReader<?, ?>> readerFactory);
|
void registerReaderFactory(String id, ReaderWriterFactory<TweedEntryReader<?, ?>> readerFactory);
|
||||||
void registerWriterFactory(String id, ReaderWriterFactory<? extends TweedEntryWriter<?, ?>> writerFactory);
|
void registerWriterFactory(String id, ReaderWriterFactory<TweedEntryWriter<?, ?>> writerFactory);
|
||||||
default void registerReaderWriterFactory(String id, ReaderWriterFactory<? extends TweedEntryReaderWriter<?, ?>> readerWriterFactory) {
|
|
||||||
registerReaderFactory(id, readerWriterFactory);
|
|
||||||
registerWriterFactory(id, readerWriterFactory);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package de.siphalor.tweed5.data.extension.api.readwrite;
|
|||||||
import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||||
|
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||||
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -43,8 +45,12 @@ public class TweedEntryReaderWriters {
|
|||||||
return TweedEntryReaderWriterImpls.STRING_READER_WRITER;
|
return TweedEntryReaderWriterImpls.STRING_READER_WRITER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, C extends ConfigEntry<T>> TweedEntryReaderWriter<T, C> nullableReaderWriter(TweedEntryReaderWriter<T, C> delegate) {
|
public static <T, C extends ConfigEntry<T>> TweedEntryReader<T, C> nullableReader(TweedEntryReader<T, C> delegate) {
|
||||||
return new TweedEntryReaderWriterImpls.NullableReaderWriter<>(delegate);
|
return new TweedEntryReaderWriterImpls.NullableReader<>(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> nullableWriter(TweedEntryWriter<T, C> delegate) {
|
||||||
|
return new TweedEntryReaderWriterImpls.NullableWriter<>(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, C extends Collection<T>> TweedEntryReaderWriter<C, CoherentCollectionConfigEntry<T, C>> coherentCollectionReaderWriter() {
|
public static <T, C extends Collection<T>> TweedEntryReaderWriter<C, CoherentCollectionConfigEntry<T, C>> coherentCollectionReaderWriter() {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package de.siphalor.tweed5.data.extension.impl;
|
package de.siphalor.tweed5.data.extension.impl;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||||
|
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 static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||||
@@ -9,22 +11,48 @@ import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWr
|
|||||||
public class DefaultTweedEntryReaderWriterImplsProvider implements TweedReaderWriterProvider {
|
public class DefaultTweedEntryReaderWriterImplsProvider implements TweedReaderWriterProvider {
|
||||||
@Override
|
@Override
|
||||||
public void provideReaderWriters(ProviderContext context) {
|
public void provideReaderWriters(ProviderContext context) {
|
||||||
context.registerReaderWriterFactory("boolean", new StaticReaderWriterFactory<>(booleanReaderWriter()));
|
StaticReaderWriterFactory<TweedEntryReader<?, ?>> booleanReaderFactory = new StaticReaderWriterFactory<>(booleanReaderWriter());
|
||||||
context.registerReaderWriterFactory("byte", new StaticReaderWriterFactory<>(byteReaderWriter()));
|
StaticReaderWriterFactory<TweedEntryWriter<?, ?>> booleanWriterFactory = new StaticReaderWriterFactory<>(booleanReaderWriter());
|
||||||
context.registerReaderWriterFactory("short", new StaticReaderWriterFactory<>(shortReaderWriter()));
|
context.registerReaderFactory("tweed5.bool", booleanReaderFactory);
|
||||||
context.registerReaderWriterFactory("int", new StaticReaderWriterFactory<>(intReaderWriter()));
|
context.registerReaderFactory("tweed5.boolean", booleanReaderFactory);
|
||||||
context.registerReaderWriterFactory("long", new StaticReaderWriterFactory<>(longReaderWriter()));
|
context.registerWriterFactory("tweed5.bool", booleanWriterFactory);
|
||||||
context.registerReaderWriterFactory("float", new StaticReaderWriterFactory<>(floatReaderWriter()));
|
context.registerWriterFactory("tweed5.boolean", booleanWriterFactory);
|
||||||
context.registerReaderWriterFactory("double", new StaticReaderWriterFactory<>(doubleReaderWriter()));
|
context.registerReaderFactory("tweed5.byte", new StaticReaderWriterFactory<>(byteReaderWriter()));
|
||||||
context.registerReaderWriterFactory("string", new StaticReaderWriterFactory<>(stringReaderWriter()));
|
context.registerWriterFactory("tweed5.byte", new StaticReaderWriterFactory<>(byteReaderWriter()));
|
||||||
context.registerReaderWriterFactory("coherent_collection", new StaticReaderWriterFactory<>(coherentCollectionReaderWriter()));
|
context.registerReaderFactory("tweed5.short", new StaticReaderWriterFactory<>(shortReaderWriter()));
|
||||||
context.registerReaderWriterFactory("compound", new StaticReaderWriterFactory<>(compoundReaderWriter()));
|
context.registerWriterFactory("tweed5.short", new StaticReaderWriterFactory<>(shortReaderWriter()));
|
||||||
|
StaticReaderWriterFactory<TweedEntryReader<?, ?>> integerReaderFactory =
|
||||||
|
new StaticReaderWriterFactory<>(intReaderWriter());
|
||||||
|
StaticReaderWriterFactory<TweedEntryWriter<?, ?>> integerWriterFactory =
|
||||||
|
new StaticReaderWriterFactory<>(intReaderWriter());
|
||||||
|
context.registerReaderFactory("tweed5.int", integerReaderFactory);
|
||||||
|
context.registerReaderFactory("tweed5.integer", integerReaderFactory);
|
||||||
|
context.registerWriterFactory("tweed5.int", integerWriterFactory);
|
||||||
|
context.registerWriterFactory("tweed5.integer", integerWriterFactory);
|
||||||
|
context.registerReaderFactory("tweed5.long", new StaticReaderWriterFactory<>(longReaderWriter()));
|
||||||
|
context.registerWriterFactory("tweed5.long", new StaticReaderWriterFactory<>(longReaderWriter()));
|
||||||
|
context.registerReaderFactory("tweed5.float", new StaticReaderWriterFactory<>(floatReaderWriter()));
|
||||||
|
context.registerWriterFactory("tweed5.float", new StaticReaderWriterFactory<>(floatReaderWriter()));
|
||||||
|
context.registerReaderFactory("tweed5.double", new StaticReaderWriterFactory<>(doubleReaderWriter()));
|
||||||
|
context.registerWriterFactory("tweed5.double", new StaticReaderWriterFactory<>(doubleReaderWriter()));
|
||||||
|
context.registerReaderFactory("tweed5.string", new StaticReaderWriterFactory<>(stringReaderWriter()));
|
||||||
|
context.registerWriterFactory("tweed5.string", new StaticReaderWriterFactory<>(stringReaderWriter()));
|
||||||
|
context.registerReaderFactory("tweed5.collection.coherent", new StaticReaderWriterFactory<>(coherentCollectionReaderWriter()));
|
||||||
|
context.registerWriterFactory("tweed5.collection.coherent", new StaticReaderWriterFactory<>(coherentCollectionReaderWriter()));
|
||||||
|
context.registerReaderFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter()));
|
||||||
|
context.registerWriterFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter()));
|
||||||
|
|
||||||
context.registerReaderWriterFactory("nullable", delegateReaderWriters -> {
|
context.registerReaderFactory("tweed5.nullable", delegateReaders -> {
|
||||||
if (delegateReaderWriters.length != 1) {
|
if (delegateReaders.length != 1) {
|
||||||
throw new IllegalArgumentException("Nullable reader writer requires a single delegate argument, got " + delegateReaderWriters.length);
|
throw new IllegalArgumentException("Nullable reader requires a single delegate argument, got " + delegateReaders.length);
|
||||||
}
|
}
|
||||||
return nullableReaderWriter(delegateReaderWriters[0]);
|
return nullableReader(delegateReaders[0]);
|
||||||
|
});
|
||||||
|
context.registerWriterFactory("tweed5.nullable", delegateWriters -> {
|
||||||
|
if (delegateWriters.length != 1) {
|
||||||
|
throw new IllegalArgumentException("Nullable writer requires a single delegate argument, got " + delegateWriters.length);
|
||||||
|
}
|
||||||
|
return nullableWriter(delegateWriters[0]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.util.Map;
|
|||||||
@AutoService(ReadWriteExtension.class)
|
@AutoService(ReadWriteExtension.class)
|
||||||
public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
||||||
|
|
||||||
|
private RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition> readerWriterDefinitionExtension;
|
||||||
private RegisteredExtensionData<EntryExtensionsData, ReadWriteEntryDataExtension> readWriteEntryDataExtension;
|
private RegisteredExtensionData<EntryExtensionsData, ReadWriteEntryDataExtension> readWriteEntryDataExtension;
|
||||||
private DefaultMiddlewareContainer<TweedEntryReader<?, ?>> entryReaderMiddlewareContainer;
|
private DefaultMiddlewareContainer<TweedEntryReader<?, ?>> entryReaderMiddlewareContainer;
|
||||||
private DefaultMiddlewareContainer<TweedEntryWriter<?, ?>> entryWriterMiddlewareContainer;
|
private DefaultMiddlewareContainer<TweedEntryWriter<?, ?>> entryWriterMiddlewareContainer;
|
||||||
@@ -44,8 +45,8 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup(TweedExtensionSetupContext context) {
|
public void setup(TweedExtensionSetupContext context) {
|
||||||
|
readerWriterDefinitionExtension = context.registerEntryExtensionData(EntryReaderWriterDefinition.class);
|
||||||
readWriteEntryDataExtension = context.registerEntryExtensionData(ReadWriteEntryDataExtension.class);
|
readWriteEntryDataExtension = context.registerEntryExtensionData(ReadWriteEntryDataExtension.class);
|
||||||
context.registerEntryExtensionData(EntryReaderWriterDefinition.class);
|
|
||||||
|
|
||||||
Collection<TweedExtension> extensions = context.configContainer().extensions();
|
Collection<TweedExtension> extensions = context.configContainer().extensions();
|
||||||
|
|
||||||
@@ -120,6 +121,11 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEntryReaderWriterDefinition(ConfigEntry<?> entry, EntryReaderWriterDefinition readerWriterDefinition) {
|
||||||
|
readerWriterDefinitionExtension.set(entry.extensionsData(), readerWriterDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadWriteContextExtensionsData createReadWriteContextExtensionsData() {
|
public ReadWriteContextExtensionsData createReadWriteContextExtensionsData() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -32,16 +32,22 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
public static final TweedEntryReaderWriter<Object, ConfigEntry<Object>> NOOP_READER_WRITER = new NoopReaderWriter();
|
public static final TweedEntryReaderWriter<Object, ConfigEntry<Object>> NOOP_READER_WRITER = new NoopReaderWriter();
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public static class NullableReaderWriter<T, C extends ConfigEntry<T>> implements TweedEntryReaderWriter<T, C> {
|
public static class NullableReader<T, C extends ConfigEntry<T>> implements TweedEntryReader<T, C> {
|
||||||
private final TweedEntryReaderWriter<T, C> delegate;
|
private final TweedEntryReader<T, C> delegate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException {
|
public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException {
|
||||||
if (reader.peekToken().isNull()) {
|
if (reader.peekToken().isNull()) {
|
||||||
|
reader.readToken();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return delegate.read(reader, entry, context);
|
return delegate.read(reader, entry, context);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class NullableWriter<T, C extends ConfigEntry<T>> implements TweedEntryWriter<T, C> {
|
||||||
|
private final TweedEntryWriter<T, C> delegate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(TweedDataVisitor writer, T value, C entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
public void write(TweedDataVisitor writer, T value, C entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||||
@@ -137,9 +143,10 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
ConfigEntry<Object> subEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
ConfigEntry<Object> subEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
||||||
TweedEntryReader<Object, ConfigEntry<Object>> subEntryReaderChain = ReadWriteExtensionImpl.getReaderChain(subEntry);
|
TweedEntryReader<Object, ConfigEntry<Object>> subEntryReaderChain = ReadWriteExtensionImpl.getReaderChain(subEntry);
|
||||||
|
if (subEntryReaderChain != null) {
|
||||||
Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context);
|
Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context);
|
||||||
entry.set(compoundValue, key, subEntryValue);
|
entry.set(compoundValue, key, subEntryValue);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new TweedEntryReadException("Unexpected token " + token + ": Expected map key or map end");
|
throw new TweedEntryReadException("Unexpected token " + token + ": Expected map key or map end");
|
||||||
}
|
}
|
||||||
@@ -159,10 +166,12 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
String key = e.getKey();
|
String key = e.getKey();
|
||||||
ConfigEntry<Object> subEntry = e.getValue();
|
ConfigEntry<Object> subEntry = e.getValue();
|
||||||
|
|
||||||
writer.visitMapEntryKey(key);
|
|
||||||
|
|
||||||
TweedEntryWriter<Object, ConfigEntry<Object>> subEntryWriterChain = ReadWriteExtensionImpl.getWriterChain(subEntry);
|
TweedEntryWriter<Object, ConfigEntry<Object>> subEntryWriterChain = ReadWriteExtensionImpl.getWriterChain(subEntry);
|
||||||
subEntryWriterChain.write(writer, entry.get(value, key), subEntry, context);
|
|
||||||
|
if (subEntryWriterChain != null) {
|
||||||
|
writer.visitMapEntryKey(key);
|
||||||
|
subEntryWriterChain.write(writer, entry.get(value, key), subEntry, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.visitMapEnd();
|
writer.visitMapEnd();
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
package de.siphalor.tweed5.utils.api.collection;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
|
public class InheritanceMap<T> {
|
||||||
|
private static final InheritanceMap<Object> EMPTY = unmodifiable(new InheritanceMap<>(Object.class));
|
||||||
|
|
||||||
|
private final Class<T> baseClass;
|
||||||
|
private final Map<T, Collection<Class<? extends T>>> instanceToClasses;
|
||||||
|
private final Map<Class<? extends T>, Collection<T>> classToInstances;
|
||||||
|
|
||||||
|
public static <T> InheritanceMap<T> empty() {
|
||||||
|
return (InheritanceMap<T>) EMPTY;
|
||||||
|
}
|
||||||
|
public static <T> InheritanceMap<T> unmodifiable(InheritanceMap<T> map) {
|
||||||
|
return new Unmodifiable<>(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InheritanceMap(Class<T> baseClass) {
|
||||||
|
this(baseClass, new IdentityHashMap<>(), new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return instanceToClasses.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return instanceToClasses.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsAnyInstanceForClass(Class<? extends T> clazz) {
|
||||||
|
return !classToInstances.getOrDefault(clazz, Collections.emptyList()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsSingleInstanceForClass(Class<? extends T> clazz) {
|
||||||
|
return classToInstances.getOrDefault(clazz, Collections.emptyList()).size() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsInstance(T instance) {
|
||||||
|
return instanceToClasses.containsKey(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V extends T> Collection<V> getAllInstances(Class<V> clazz) {
|
||||||
|
return (Collection<V>) classToInstances.getOrDefault(clazz, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V extends T> V getSingleInstance(Class<V> clazz) throws NonUniqueResultException {
|
||||||
|
Collection<T> instances = classToInstances.getOrDefault(clazz, Collections.emptyList());
|
||||||
|
if (instances.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else if (instances.size() == 1) {
|
||||||
|
return (V) instances.iterator().next();
|
||||||
|
} else {
|
||||||
|
throw new NonUniqueResultException("Multiple instances for class " + clazz.getName() + " exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean putAll(T... instances) {
|
||||||
|
boolean changed = false;
|
||||||
|
for (T instance : instances) {
|
||||||
|
changed = put(instance) || changed;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean put(T instance) {
|
||||||
|
if (instanceToClasses.containsKey(instance)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
putInternal(instance);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean putIfAbsent(T instance) {
|
||||||
|
Collection<T> existingInstances = classToInstances.getOrDefault(instance.getClass(), Collections.emptyList());
|
||||||
|
if (existingInstances.isEmpty()) {
|
||||||
|
putInternal(instance);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V extends T> V removeInstance(V instance) {
|
||||||
|
if (!instanceToClasses.containsKey(instance)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Collection<Class<? extends T>> classes = instanceToClasses.getOrDefault(instance, Collections.emptyList());
|
||||||
|
for (Class<? extends T> implemented : classes) {
|
||||||
|
classToInstances.getOrDefault(implemented, Collections.emptyList()).remove(instance);
|
||||||
|
}
|
||||||
|
instanceToClasses.remove(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
instanceToClasses.clear();
|
||||||
|
classToInstances.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<T> values() {
|
||||||
|
return instanceToClasses.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putInternal(T instance) {
|
||||||
|
Collection<Class<? extends T>> classes = findClasses((Class<? extends T>) instance.getClass());
|
||||||
|
|
||||||
|
instanceToClasses.put(instance, classes);
|
||||||
|
for (Class<? extends T> implementedClass : classes) {
|
||||||
|
classToInstances.computeIfAbsent(implementedClass, c -> new ArrayList<>()).add(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Class<? extends T>> findClasses(Class<? extends T> clazz) {
|
||||||
|
List<Class<? extends T>> classes = new ArrayList<>();
|
||||||
|
|
||||||
|
Class<?> superClass = clazz;
|
||||||
|
while (superClass != Object.class && superClass != baseClass && baseClass.isAssignableFrom(superClass)) {
|
||||||
|
classes.add((Class<? extends T>) superClass);
|
||||||
|
|
||||||
|
if (baseClass == Object.class || baseClass.isInterface()) {
|
||||||
|
classes.addAll(findOnlyInterfaces((Class<? extends T>) superClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
superClass = superClass.getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Class<? extends T>> findOnlyInterfaces(Class<? extends T> clazz) {
|
||||||
|
List<Class<? extends T>> classes = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Class<?> implemented : clazz.getInterfaces()) {
|
||||||
|
if (baseClass != implemented && baseClass.isAssignableFrom(implemented)) {
|
||||||
|
classes.add((Class<? extends T>) implemented);
|
||||||
|
classes.addAll(findOnlyInterfaces((Class<? extends T>) implemented));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NonUniqueResultException extends Exception {
|
||||||
|
public NonUniqueResultException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Unmodifiable<T> extends InheritanceMap<T> {
|
||||||
|
public Unmodifiable(InheritanceMap<T> delegate) {
|
||||||
|
super(delegate.baseClass, delegate.instanceToClasses, delegate.classToInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean put(T instance) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean putIfAbsent(T instance) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V extends T> V removeInstance(V instance) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package de.siphalor.tweed5.utils.api.collection;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class InheritanceMapTest {
|
||||||
|
@Test
|
||||||
|
void full() {
|
||||||
|
InheritanceMap<Object> map = new InheritanceMap<>(Object.class);
|
||||||
|
map.put(123L);
|
||||||
|
map.put(123);
|
||||||
|
map.put(456);
|
||||||
|
|
||||||
|
assertThat(map.getAllInstances(Long.class)).containsExactlyInAnyOrder(123L);
|
||||||
|
assertThat(map.getAllInstances(Integer.class)).containsExactlyInAnyOrder(123, 456);
|
||||||
|
assertThat(map.getAllInstances(Number.class)).containsExactlyInAnyOrder(123L, 123, 456);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
tweed5-weaver-pojo-serde-extension/build.gradle.kts
Normal file
6
tweed5-weaver-pojo-serde-extension/build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
dependencies {
|
||||||
|
api(project(":tweed5-weaver-pojo"))
|
||||||
|
api(project(":tweed5-serde-extension"))
|
||||||
|
|
||||||
|
testImplementation(project(":tweed5-serde-hjson"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code <spec> = <id> [ "(" <spec> ( "," <spec> )* ")" ] }
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface EntryReadWriteConfig {
|
||||||
|
String value() default "";
|
||||||
|
String writer() default "";
|
||||||
|
String reader() default "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import de.siphalor.tweed5.data.extension.api.*;
|
||||||
|
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
||||||
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class ReadWritePojoPostProcessor implements TweedPojoWeavingPostProcessor {
|
||||||
|
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryReader<?, ?>>> readerFactories = new HashMap<>();
|
||||||
|
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryWriter<?, ?>>> writerFactories = new HashMap<>();
|
||||||
|
|
||||||
|
public ReadWritePojoPostProcessor() {
|
||||||
|
loadProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadProviders() {
|
||||||
|
ServiceLoader<TweedReaderWriterProvider> serviceLoader = ServiceLoader.load(TweedReaderWriterProvider.class);
|
||||||
|
|
||||||
|
for (TweedReaderWriterProvider readerWriterProvider : serviceLoader) {
|
||||||
|
TweedReaderWriterProvider.ProviderContext providerContext = new TweedReaderWriterProvider.ProviderContext() {
|
||||||
|
@Override
|
||||||
|
public void registerReaderFactory(
|
||||||
|
String id,
|
||||||
|
TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryReader<?, ?>> readerFactory
|
||||||
|
) {
|
||||||
|
if (readerFactories.putIfAbsent(id, readerFactory) != null) {
|
||||||
|
log.warn(
|
||||||
|
"Found duplicate Tweed entry reader id \"{}\" in provider class {}",
|
||||||
|
id,
|
||||||
|
readerWriterProvider.getClass().getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerWriterFactory(
|
||||||
|
String id,
|
||||||
|
TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryWriter<?, ?>> writerFactory
|
||||||
|
) {
|
||||||
|
if (writerFactories.putIfAbsent(id, writerFactory) != null) {
|
||||||
|
log.warn(
|
||||||
|
"Found duplicate Tweed entry writer id \"{}\" in provider class {}",
|
||||||
|
id,
|
||||||
|
readerWriterProvider.getClass().getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
readerWriterProvider.provideReaderWriters(providerContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(ConfigEntry<?> configEntry, WeavingContext context) {
|
||||||
|
EntryReadWriteConfig entryConfig = context.annotations().getAnnotation(EntryReadWriteConfig.class);
|
||||||
|
if (entryConfig == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadWriteExtension readWriteExtension = context.configContainer().extension(ReadWriteExtension.class);
|
||||||
|
if (readWriteExtension == null) {
|
||||||
|
log.error("You must not use {} without the {}", this.getClass().getSimpleName(), ReadWriteExtension.class.getSimpleName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
readWriteExtension.setEntryReaderWriterDefinition(configEntry, createDefinitionFromEntryConfig(entryConfig, context));
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntryReaderWriterDefinition createDefinitionFromEntryConfig(EntryReadWriteConfig entryConfig, WeavingContext context) {
|
||||||
|
String readerSpecText = entryConfig.reader().isEmpty() ? entryConfig.value() : entryConfig.reader();
|
||||||
|
String writerSpecText = entryConfig.writer().isEmpty() ? entryConfig.value() : entryConfig.writer();
|
||||||
|
|
||||||
|
SerdePojoReaderWriterSpec readerSpec;
|
||||||
|
SerdePojoReaderWriterSpec writerSpec;
|
||||||
|
if (readerSpecText.equals(writerSpecText)) {
|
||||||
|
readerSpec = writerSpec = specFromText(readerSpecText, context);
|
||||||
|
} else {
|
||||||
|
readerSpec = specFromText(readerSpecText, context);
|
||||||
|
writerSpec = specFromText(writerSpecText, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
TweedEntryReader<?, ?> reader = readerSpec == null
|
||||||
|
? TweedEntryReaderWriterImpls.NOOP_READER_WRITER
|
||||||
|
: resolveReaderWriterFromSpec((Class<TweedEntryReader<?, ?>>)(Object) TweedEntryReader.class, readerFactories, readerSpec, context);
|
||||||
|
//noinspection unchecked
|
||||||
|
TweedEntryWriter<?, ?> writer = writerSpec == null
|
||||||
|
? TweedEntryReaderWriterImpls.NOOP_READER_WRITER
|
||||||
|
: resolveReaderWriterFromSpec((Class<TweedEntryWriter<?, ?>>)(Object) TweedEntryWriter.class, writerFactories, writerSpec, context);
|
||||||
|
|
||||||
|
return new EntryReaderWriterDefinition() {
|
||||||
|
@Override
|
||||||
|
public TweedEntryReader<?, ?> reader() {
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TweedEntryWriter<?, ?> writer() {
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private SerdePojoReaderWriterSpec specFromText(String specText, WeavingContext context) {
|
||||||
|
if (specText.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return SerdePojoReaderWriterSpec.parse(specText);
|
||||||
|
} catch (SerdePojoReaderWriterSpec.ParseException e) {
|
||||||
|
log.warn(
|
||||||
|
"Failed to parse definition for reader or writer on entry {}, entry will not be included in serde",
|
||||||
|
context.path(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T resolveReaderWriterFromSpec(
|
||||||
|
Class<T> baseClass,
|
||||||
|
Map<String, TweedReaderWriterProvider.ReaderWriterFactory<T>> factories,
|
||||||
|
SerdePojoReaderWriterSpec spec,
|
||||||
|
WeavingContext context
|
||||||
|
) {
|
||||||
|
//noinspection unchecked
|
||||||
|
T[] arguments = spec.arguments()
|
||||||
|
.stream()
|
||||||
|
.map(argSpec -> resolveReaderWriterFromSpec(baseClass, factories, argSpec, context))
|
||||||
|
.toArray(length -> (T[]) Array.newInstance(baseClass, length));
|
||||||
|
|
||||||
|
TweedReaderWriterProvider.ReaderWriterFactory<T> factory = factories.get(spec.identifier());
|
||||||
|
|
||||||
|
T instance;
|
||||||
|
if (factory != null) {
|
||||||
|
instance = factory.create(arguments);
|
||||||
|
} else {
|
||||||
|
instance = loadClassIfExists(baseClass, spec.identifier(), arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance == null) {
|
||||||
|
log.warn(
|
||||||
|
"Failed to resolve reader or writer factory \"{}\" for entry {}, entry will not be included in serde",
|
||||||
|
spec.identifier(),
|
||||||
|
context.path()
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T loadClassIfExists(Class<T> baseClass, String className, T[] arguments) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(className);
|
||||||
|
Class<?>[] argClassses = new Class<?>[arguments.length];
|
||||||
|
Arrays.fill(argClassses, baseClass);
|
||||||
|
|
||||||
|
Constructor<?> constructor = clazz.getConstructor(argClassses);
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) constructor.newInstance((Object[]) arguments);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
||||||
|
log.warn("Failed to instantiate class {}", className, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojoext.serde.impl;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.PrimitiveIterator;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class SerdePojoReaderWriterSpec {
|
||||||
|
String identifier;
|
||||||
|
List<SerdePojoReaderWriterSpec> arguments;
|
||||||
|
|
||||||
|
public static SerdePojoReaderWriterSpec parse(String input) throws ParseException {
|
||||||
|
Lexer lexer = new Lexer(input.codePoints().iterator());
|
||||||
|
SerdePojoReaderWriterSpec spec = parseSpec(lexer);
|
||||||
|
lexer.chompWhitespace();
|
||||||
|
int codePoint = lexer.nextCodePoint();
|
||||||
|
if (codePoint != -1) {
|
||||||
|
throw lexer.createException("Found trailing text after spec", codePoint);
|
||||||
|
}
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SerdePojoReaderWriterSpec parseSpec(Lexer lexer) throws ParseException {
|
||||||
|
lexer.chompWhitespace();
|
||||||
|
String identifier = lexer.nextIdentifier();
|
||||||
|
lexer.chompWhitespace();
|
||||||
|
int codePoint = lexer.peekCodePoint();
|
||||||
|
if (codePoint == '(') {
|
||||||
|
lexer.nextCodePoint();
|
||||||
|
lexer.chompWhitespace();
|
||||||
|
if (lexer.peekCodePoint() == ')') {
|
||||||
|
lexer.nextCodePoint();
|
||||||
|
return new SerdePojoReaderWriterSpec(identifier, Collections.emptyList());
|
||||||
|
}
|
||||||
|
SerdePojoReaderWriterSpec spec = new SerdePojoReaderWriterSpec(identifier, parseSpecList(lexer));
|
||||||
|
codePoint = lexer.nextCodePoint();
|
||||||
|
if (codePoint != ')') {
|
||||||
|
throw lexer.createException("Argument list must be ended with a closing parenthesis", codePoint);
|
||||||
|
}
|
||||||
|
return spec;
|
||||||
|
} else {
|
||||||
|
return new SerdePojoReaderWriterSpec(identifier, Collections.emptyList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<SerdePojoReaderWriterSpec> parseSpecList(Lexer lexer) throws ParseException {
|
||||||
|
List<SerdePojoReaderWriterSpec> specs = new ArrayList<>();
|
||||||
|
while (true) {
|
||||||
|
specs.add(parseSpec(lexer));
|
||||||
|
lexer.chompWhitespace();
|
||||||
|
int codePoint = lexer.peekCodePoint();
|
||||||
|
if (codePoint != ',') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lexer.nextCodePoint();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(specs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class Lexer {
|
||||||
|
private static final int EMPTY = -2;
|
||||||
|
private final PrimitiveIterator.OfInt codePointIterator;
|
||||||
|
private int peek = EMPTY;
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
public String nextIdentifier() throws ParseException {
|
||||||
|
int codePoint = nextCodePoint();
|
||||||
|
if (codePoint == -1) {
|
||||||
|
throw createException("Expected identifier, got end of input", codePoint);
|
||||||
|
} else if (!isIdentifierChar(codePoint)) {
|
||||||
|
throw createException("Expected identifier (alphanumeric character)", codePoint);
|
||||||
|
}
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.appendCodePoint(codePoint);
|
||||||
|
boolean dot = false;
|
||||||
|
while ((codePoint = peekCodePoint()) >= 0) {
|
||||||
|
if (isIdentifierChar(codePoint)) {
|
||||||
|
stringBuilder.appendCodePoint(nextCodePoint());
|
||||||
|
dot = false;
|
||||||
|
} else if (codePoint == '.') {
|
||||||
|
if (dot) {
|
||||||
|
throw createException("Unexpected double dot in identifier", codePoint);
|
||||||
|
} else {
|
||||||
|
stringBuilder.appendCodePoint(nextCodePoint());
|
||||||
|
dot = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dot) {
|
||||||
|
throw createException("Identifier must not end with dot", codePoint);
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isIdentifierChar(int codePoint) {
|
||||||
|
return (codePoint >= '0' && codePoint <= '9')
|
||||||
|
|| (codePoint >= 'a' && codePoint <= 'z')
|
||||||
|
|| (codePoint >= 'A' && codePoint <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
public void chompWhitespace() {
|
||||||
|
while (Character.isWhitespace(peekCodePoint())) {
|
||||||
|
nextCodePoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int peekCodePoint() {
|
||||||
|
if (peek == EMPTY) {
|
||||||
|
peek = nextCodePoint();
|
||||||
|
}
|
||||||
|
return peek;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int nextCodePoint() {
|
||||||
|
if (peek != EMPTY) {
|
||||||
|
int codePoint = peek;
|
||||||
|
peek = EMPTY;
|
||||||
|
return codePoint;
|
||||||
|
}
|
||||||
|
if (codePointIterator.hasNext()) {
|
||||||
|
index++;
|
||||||
|
return codePointIterator.nextInt();
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParseException createException(String message, int codePoint) {
|
||||||
|
return new ParseException(message, index, codePoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public static class ParseException extends Exception {
|
||||||
|
private final int index;
|
||||||
|
private final int codePoint;
|
||||||
|
|
||||||
|
public ParseException(String message, int index, int codePoint) {
|
||||||
|
super(message);
|
||||||
|
this.index = index;
|
||||||
|
this.codePoint = codePoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
String message = super.getMessage();
|
||||||
|
StringBuilder stringBuilder = new StringBuilder(30 + message.length())
|
||||||
|
.append("Parse error at index ")
|
||||||
|
.append(index)
|
||||||
|
.append(" \"");
|
||||||
|
if (codePoint == -1) {
|
||||||
|
stringBuilder.append("EOF");
|
||||||
|
} else {
|
||||||
|
stringBuilder.appendCodePoint(codePoint);
|
||||||
|
}
|
||||||
|
return stringBuilder
|
||||||
|
.append("\": ")
|
||||||
|
.append(message)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojoext.serde;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import de.siphalor.tweed5.data.extension.api.*;
|
||||||
|
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||||
|
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||||
|
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||||
|
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||||
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
|
||||||
|
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 org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class WeaverPojoSerdeExtensionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
void testAnnotated() {
|
||||||
|
TweedPojoWeaverBootstrapper<AnnotatedConfig> weaverBootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class);
|
||||||
|
|
||||||
|
ConfigContainer<AnnotatedConfig> configContainer = weaverBootstrapper.weave();
|
||||||
|
configContainer.initialize();
|
||||||
|
|
||||||
|
ReadWriteExtension readWriteExtension = configContainer.extension(ReadWriteExtension.class);
|
||||||
|
assertThat(readWriteExtension).isNotNull();
|
||||||
|
|
||||||
|
AnnotatedConfig config = new AnnotatedConfig(123, "test", new TestClass(987));
|
||||||
|
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
HjsonWriter hjsonWriter = new HjsonWriter(stringWriter, new HjsonWriter.Options());
|
||||||
|
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");
|
||||||
|
|
||||||
|
HjsonReader reader = new HjsonReader(new HjsonLexer(new StringReader(
|
||||||
|
"{\n\tanInt: 987\n\ttext: abdef\n\ttest: { inner: 29 }\n}"
|
||||||
|
)));
|
||||||
|
assertThat(readWriteExtension.read(
|
||||||
|
reader,
|
||||||
|
configContainer.rootEntry(),
|
||||||
|
readWriteExtension.createReadWriteContextExtensionsData()
|
||||||
|
)).isEqualTo(new AnnotatedConfig(987, "abdef", new TestClass(29)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoService(TweedReaderWriterProvider.class)
|
||||||
|
public static class TestWriterProvider implements TweedReaderWriterProvider {
|
||||||
|
@Override
|
||||||
|
public void provideReaderWriters(ProviderContext context) {
|
||||||
|
context.registerWriterFactory("tweed5.test.dummy", delegates -> new TweedEntryWriter<Object, ConfigEntry<Object>>() {
|
||||||
|
@Override
|
||||||
|
public void write(
|
||||||
|
TweedDataVisitor writer,
|
||||||
|
Object value,
|
||||||
|
ConfigEntry<Object> entry,
|
||||||
|
TweedWriteContext context
|
||||||
|
) throws TweedDataWriteException {
|
||||||
|
writer.visitString("my cool custom writer");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PojoWeaving(extensions = ReadWriteExtension.class, postProcessors = ReadWritePojoPostProcessor.class)
|
||||||
|
@CompoundWeaving
|
||||||
|
@EntryReadWriteConfig("tweed5.compound")
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@ToString
|
||||||
|
public static class AnnotatedConfig {
|
||||||
|
@EntryReadWriteConfig("tweed5.integer")
|
||||||
|
public int anInt;
|
||||||
|
|
||||||
|
@EntryReadWriteConfig("tweed5.nullable(tweed5.string)")
|
||||||
|
public String text;
|
||||||
|
|
||||||
|
@EntryReadWriteConfig(writer = "tweed5.test.dummy", reader = "tweed5.compound")
|
||||||
|
@CompoundWeaving
|
||||||
|
public TestClass test;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@ToString
|
||||||
|
public static class TestClass {
|
||||||
|
@EntryReadWriteConfig("tweed5.integer")
|
||||||
|
public int inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojoext.serde.impl;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||||
|
|
||||||
|
class SerdePojoReaderWriterSpecTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(ignoreLeadingAndTrailingWhitespace = false, value = {
|
||||||
|
" abc ,abc",
|
||||||
|
" abc() ,abc",
|
||||||
|
" abc.123 ,abc.123",
|
||||||
|
"abc.123 ( ) ,abc.123",
|
||||||
|
"123.abc,123.abc",
|
||||||
|
})
|
||||||
|
@SneakyThrows
|
||||||
|
void parseSimpleIdentifier(String input, String identifier) {
|
||||||
|
SerdePojoReaderWriterSpec spec = SerdePojoReaderWriterSpec.parse(input);
|
||||||
|
assertThat(spec.identifier()).isEqualTo(identifier);
|
||||||
|
assertThat(spec.arguments()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
void parseNested() {
|
||||||
|
SerdePojoReaderWriterSpec spec = SerdePojoReaderWriterSpec.parse("abc.def ( 12 ( def, ghi ( ) ), jkl ) ");
|
||||||
|
assertThat(spec).isEqualTo(new SerdePojoReaderWriterSpec("abc.def", Arrays.asList(
|
||||||
|
new SerdePojoReaderWriterSpec("12", Arrays.asList(
|
||||||
|
new SerdePojoReaderWriterSpec("def", Collections.emptyList()),
|
||||||
|
new SerdePojoReaderWriterSpec("ghi", Collections.emptyList())
|
||||||
|
)),
|
||||||
|
new SerdePojoReaderWriterSpec("jkl", Collections.emptyList())
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(ignoreLeadingAndTrailingWhitespace = false, nullValues = "EOF", delimiter = ';', value = {
|
||||||
|
" abc def ;6;d",
|
||||||
|
"abcäöüdef;4;ä",
|
||||||
|
"abc.def(;8;EOF",
|
||||||
|
"'';0;EOF",
|
||||||
|
",;1;,",
|
||||||
|
"abc(,);5;,",
|
||||||
|
"abc..def;5;.",
|
||||||
|
})
|
||||||
|
@SneakyThrows
|
||||||
|
void parseError(String input, int index, String codePoint) {
|
||||||
|
assertThatThrownBy(() -> SerdePojoReaderWriterSpec.parse(input))
|
||||||
|
.asInstanceOf(type(SerdePojoReaderWriterSpec.ParseException.class))
|
||||||
|
.isInstanceOf(SerdePojoReaderWriterSpec.ParseException.class)
|
||||||
|
.satisfies(
|
||||||
|
exception -> assertThat(exception.index()).as("index of: " + exception.getMessage()).isEqualTo(index),
|
||||||
|
exception -> assertThat(exception.codePoint()).as("code point of: " + exception.getMessage())
|
||||||
|
.isEqualTo(codePoint == null ? -1 : codePoint.codePointAt(0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(project(":tweed5-core"))
|
api(project(":tweed5-core"))
|
||||||
api(project(":tweed5-naming-format"))
|
api(project(":tweed5-naming-format"))
|
||||||
compileOnly(project(":tweed5-default-extensions"))
|
|
||||||
compileOnly(project(":tweed5-serde-extension"))
|
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
|
|||||||
* Marks this class as a class that should be woven as a {@link de.siphalor.tweed5.core.api.entry.CompoundConfigEntry}.
|
* Marks this class as a class that should be woven as a {@link de.siphalor.tweed5.core.api.entry.CompoundConfigEntry}.
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||||
public @interface CompoundWeaving {
|
public @interface CompoundWeaving {
|
||||||
/**
|
/**
|
||||||
* The naming format to use for this POJO.
|
* The naming format to use for this POJO.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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.TweedPojoWeaver;
|
||||||
|
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;
|
||||||
@@ -22,5 +23,7 @@ public @interface PojoWeaving {
|
|||||||
TrivialPojoWeaver.class,
|
TrivialPojoWeaver.class,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Class<? extends TweedPojoWeavingPostProcessor>[] postProcessors() default {};
|
||||||
|
|
||||||
Class<? extends TweedExtension>[] extensions() default {};
|
Class<? extends TweedExtension>[] extensions() default {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Repeatable;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents multi-level annotations across multiple Java elements.
|
||||||
|
* E.g. annotations on a field overriding annotations declared on the field type.
|
||||||
|
*/
|
||||||
|
public class Annotations {
|
||||||
|
private static final List<ElementType> ELEMENT_TYPE_ORDER = Arrays.asList(
|
||||||
|
ElementType.TYPE_USE,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.LOCAL_VARIABLE,
|
||||||
|
ElementType.TYPE_PARAMETER,
|
||||||
|
ElementType.TYPE,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.PACKAGE
|
||||||
|
);
|
||||||
|
private final Map<ElementType, AnnotatedElement> elements = new EnumMap<>(ElementType.class);
|
||||||
|
|
||||||
|
public void addAnnotationsFrom(ElementType elementType, AnnotatedElement element) {
|
||||||
|
elements.put(elementType, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||||
|
for (ElementType elementType : ELEMENT_TYPE_ORDER) {
|
||||||
|
AnnotatedElement annotatedElement = elements.get(elementType);
|
||||||
|
if (annotatedElement != null) {
|
||||||
|
T annotation = annotatedElement.getAnnotation(annotationClass);
|
||||||
|
if (annotation != null) {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public <T extends Annotation> T getAnnotation(ElementType elementType, Class<T> annotationType) {
|
||||||
|
AnnotatedElement annotatedElement = elements.get(elementType);
|
||||||
|
if (annotatedElement == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return annotatedElement.getAnnotation(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public <T extends Annotation> T[] getAnnotationHierarchy(Class<T> annotationClass) {
|
||||||
|
List<T> hierarchy = new ArrayList<>(elements.size());
|
||||||
|
for (ElementType elementType : ELEMENT_TYPE_ORDER) {
|
||||||
|
AnnotatedElement element = elements.get(elementType);
|
||||||
|
if (element != null) {
|
||||||
|
T annotation = element.getAnnotation(annotationClass);
|
||||||
|
if (annotation != null) {
|
||||||
|
hierarchy.add(annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return hierarchy.toArray((T[]) Array.newInstance(annotationClass, hierarchy.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public <T extends Annotation> T[] getAnnotations(Class<T> annotationClass) {
|
||||||
|
for (ElementType elementType : ELEMENT_TYPE_ORDER) {
|
||||||
|
AnnotatedElement annotatedElement = elements.get(elementType);
|
||||||
|
if (annotatedElement != null) {
|
||||||
|
T[] annotations = annotatedElement.getAnnotationsByType(annotationClass);
|
||||||
|
if (annotations.length != 0) {
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T[]) Array.newInstance(annotationClass, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public <T extends Annotation> T[] getAnnotations(ElementType elementType, Class<T> annotationType) {
|
||||||
|
AnnotatedElement annotatedElement = elements.get(elementType);
|
||||||
|
if (annotatedElement == null) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T[]) Array.newInstance(annotationType, 0);
|
||||||
|
}
|
||||||
|
return annotatedElement.getAnnotationsByType(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public <T extends Annotation> T[][] getAnnotationsHierachy(Class<T> annotationClass) {
|
||||||
|
List<T[]> hierarchy = new ArrayList<>(ELEMENT_TYPE_ORDER.size());
|
||||||
|
for (ElementType elementType : ELEMENT_TYPE_ORDER) {
|
||||||
|
AnnotatedElement annotatedElement = elements.get(elementType);
|
||||||
|
if (annotatedElement != null) {
|
||||||
|
T[] annotations = annotatedElement.getAnnotationsByType(annotationClass);
|
||||||
|
if (annotations.length != 0) {
|
||||||
|
hierarchy.add(annotations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return hierarchy.toArray((T[][]) Array.newInstance(annotationClass, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Annotation[] getAllAnnotations() {
|
||||||
|
Map<Class<? extends Annotation>, Annotation[]> annotations = new HashMap<>();
|
||||||
|
for (ElementType elementType : ELEMENT_TYPE_ORDER) {
|
||||||
|
AnnotatedElement annotatedElement = elements.get(elementType);
|
||||||
|
if (annotatedElement != null) {
|
||||||
|
for (Annotation annotation : annotatedElement.getAnnotations()) {
|
||||||
|
annotations.putIfAbsent(annotation.annotationType(), new Annotation[]{annotation});
|
||||||
|
|
||||||
|
Repeatable repeatable = annotation.annotationType().getAnnotation(Repeatable.class);
|
||||||
|
if (repeatable != null) {
|
||||||
|
annotations.put(repeatable.value(), annotatedElement.getAnnotationsByType(repeatable.value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (annotations.isEmpty()) {
|
||||||
|
return new Annotation[0];
|
||||||
|
} else if (annotations.size() == 1) {
|
||||||
|
return annotations.values().iterator().next();
|
||||||
|
} else {
|
||||||
|
return annotations.values().stream().flatMap(Arrays::stream).toArray(Annotation[]::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||||
|
|
||||||
import de.siphalor.tweed5.core.api.collection.TypedMultimap;
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||||
import de.siphalor.tweed5.namingformat.api.NamingFormat;
|
import de.siphalor.tweed5.namingformat.api.NamingFormat;
|
||||||
@@ -8,19 +7,18 @@ import de.siphalor.tweed5.namingformat.api.NamingFormatCollector;
|
|||||||
import de.siphalor.tweed5.namingformat.api.NamingFormats;
|
import de.siphalor.tweed5.namingformat.api.NamingFormats;
|
||||||
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.entry.WeavableCompoundConfigEntry;
|
import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCompoundConfigEntry;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.impl.entry.StaticPojoCompoundConfigEntry;
|
||||||
import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoClassIntrospector;
|
import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoClassIntrospector;
|
||||||
import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoWeavingException;
|
import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoWeavingException;
|
||||||
import de.siphalor.tweed5.weaver.pojo.impl.entry.StaticPojoCompoundConfigEntry;
|
|
||||||
import de.siphalor.tweed5.weaver.pojo.impl.weaving.compound.CompoundWeavingConfig;
|
import de.siphalor.tweed5.weaver.pojo.impl.weaving.compound.CompoundWeavingConfig;
|
||||||
import de.siphalor.tweed5.weaver.pojo.impl.weaving.compound.CompoundWeavingConfigImpl;
|
import de.siphalor.tweed5.weaver.pojo.impl.weaving.compound.CompoundWeavingConfigImpl;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.util.Map;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -43,11 +41,11 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable <T> ConfigEntry<T> weaveEntry(Class<T> valueClass, WeavingContext context) {
|
public @Nullable <T> ConfigEntry<T> weaveEntry(Class<T> valueClass, WeavingContext context) {
|
||||||
if (!valueClass.isAnnotationPresent(CompoundWeaving.class)) {
|
if (context.annotations().getAnnotation(CompoundWeaving.class) == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
CompoundWeavingConfig weavingConfig = getOrCreateWeavingConfig(valueClass, context);
|
CompoundWeavingConfig weavingConfig = getOrCreateWeavingConfig(context);
|
||||||
WeavingContext.ExtensionsData newExtensionsData = context.extensionsData().copy();
|
WeavingContext.ExtensionsData newExtensionsData = context.extensionsData().copy();
|
||||||
weavingConfigAccess.set(newExtensionsData, weavingConfig);
|
weavingConfigAccess.set(newExtensionsData, weavingConfig);
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompoundWeavingConfig getOrCreateWeavingConfig(Class<?> valueClass, WeavingContext context) {
|
private CompoundWeavingConfig getOrCreateWeavingConfig(WeavingContext context) {
|
||||||
CompoundWeavingConfig parent;
|
CompoundWeavingConfig parent;
|
||||||
if (context.extensionsData().isPatchworkPartSet(CompoundWeavingConfig.class)) {
|
if (context.extensionsData().isPatchworkPartSet(CompoundWeavingConfig.class)) {
|
||||||
parent = (CompoundWeavingConfig) context.extensionsData();
|
parent = (CompoundWeavingConfig) context.extensionsData();
|
||||||
@@ -76,7 +74,7 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
|||||||
parent = DEFAULT_WEAVING_CONFIG;
|
parent = DEFAULT_WEAVING_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompoundWeavingConfig local = getWeavingConfigFromClassAnnotation(valueClass);
|
CompoundWeavingConfig local = createWeavingConfigFromAnnotations(context.annotations());
|
||||||
if (local == null) {
|
if (local == null) {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
@@ -91,23 +89,21 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
|||||||
WeavingContext parentContext
|
WeavingContext parentContext
|
||||||
) {
|
) {
|
||||||
return parentContext.subContextBuilder(name)
|
return parentContext.subContextBuilder(name)
|
||||||
.additionalData(createAdditionalDataFromAnnotations(property.field().getAnnotations()))
|
.annotations(collectAnnotationsForField(property.field()))
|
||||||
.extensionsData(newExtensionsData)
|
.extensionsData(newExtensionsData)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedMultimap<Object> createAdditionalDataFromAnnotations(Annotation[] annotations) {
|
private Annotations collectAnnotationsForField(Field field) {
|
||||||
if (annotations.length == 0) {
|
Annotations annotations = new Annotations();
|
||||||
return TypedMultimap.empty();
|
annotations.addAnnotationsFrom(ElementType.TYPE, field.getType());
|
||||||
}
|
annotations.addAnnotationsFrom(ElementType.FIELD, field);
|
||||||
TypedMultimap<Object> additionalData = new TypedMultimap<>(new HashMap<>(), ArrayList::new);
|
return annotations;
|
||||||
Collections.addAll(additionalData, annotations);
|
|
||||||
return TypedMultimap.unmodifiable(additionalData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private CompoundWeavingConfig getWeavingConfigFromClassAnnotation(Class<?> clazz) {
|
private CompoundWeavingConfig createWeavingConfigFromAnnotations(Annotations annotations) {
|
||||||
CompoundWeaving annotation = clazz.getAnnotation(CompoundWeaving.class);
|
CompoundWeaving annotation = annotations.getAnnotation(CompoundWeaving.class);
|
||||||
if (annotation == null) {
|
if (annotation == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
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.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||||
import de.siphalor.tweed5.core.api.collection.TypedMultimap;
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -14,26 +14,30 @@ import java.util.Arrays;
|
|||||||
public class WeavingContext implements TweedPojoWeavingFunction.NonNull {
|
public class WeavingContext implements TweedPojoWeavingFunction.NonNull {
|
||||||
@Nullable
|
@Nullable
|
||||||
WeavingContext parent;
|
WeavingContext parent;
|
||||||
ExtensionsData extensionsData;
|
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
|
@NotNull
|
||||||
TweedPojoWeavingFunction.NonNull weavingFunction;
|
TweedPojoWeavingFunction.NonNull weavingFunction;
|
||||||
|
@NotNull
|
||||||
|
ConfigContainer<?> configContainer;
|
||||||
|
@NotNull
|
||||||
String[] path;
|
String[] path;
|
||||||
TypedMultimap<Object> additionalData;
|
@NotNull
|
||||||
|
ExtensionsData extensionsData;
|
||||||
|
@NotNull
|
||||||
|
Annotations annotations;
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer<?> configContainer) {
|
||||||
return new Builder(null, new String[0]);
|
return new Builder(null, weavingFunction, configContainer, new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder(String baseName) {
|
public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer<?> configContainer, String baseName) {
|
||||||
return new Builder(null, new String[]{ baseName });
|
return new Builder(null, weavingFunction, configContainer, new String[]{ baseName });
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder subContextBuilder(String subPathName) {
|
public Builder subContextBuilder(String subPathName) {
|
||||||
String[] newPath = Arrays.copyOf(path, path.length + 1);
|
String[] newPath = Arrays.copyOf(path, path.length + 1);
|
||||||
newPath[path.length] = subPathName;
|
newPath[path.length] = subPathName;
|
||||||
return new Builder(this, newPath)
|
return new Builder(this, weavingFunction, configContainer, newPath).extensionsData(extensionsData);
|
||||||
.extensionsData(extensionsData)
|
|
||||||
.weavingFunction(weavingFunction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,18 +53,20 @@ public class WeavingContext implements TweedPojoWeavingFunction.NonNull {
|
|||||||
public static class Builder {
|
public static class Builder {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final WeavingContext parent;
|
private final WeavingContext parent;
|
||||||
|
private final TweedPojoWeavingFunction.NonNull weavingFunction;
|
||||||
|
private final ConfigContainer<?> configContainer;
|
||||||
private final String[] path;
|
private final String[] path;
|
||||||
private ExtensionsData extensionsData;
|
private ExtensionsData extensionsData;
|
||||||
private TweedPojoWeavingFunction.NonNull weavingFunction;
|
private Annotations annotations;
|
||||||
private TypedMultimap<Object> additionalData;
|
|
||||||
|
|
||||||
public WeavingContext build() {
|
public WeavingContext build() {
|
||||||
return new WeavingContext(
|
return new WeavingContext(
|
||||||
parent,
|
parent,
|
||||||
extensionsData,
|
|
||||||
weavingFunction,
|
weavingFunction,
|
||||||
|
configContainer,
|
||||||
path,
|
path,
|
||||||
additionalData
|
extensionsData,
|
||||||
|
annotations
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext;
|
||||||
|
|
||||||
|
public interface TweedPojoWeavingPostProcessor {
|
||||||
|
void apply(ConfigEntry<?> configEntry, WeavingContext context);
|
||||||
|
}
|
||||||
@@ -9,13 +9,13 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> implements WeavableCompoundConfigEntry<T> {
|
public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> implements WeavableCompoundConfigEntry<T> {
|
||||||
private final MethodHandle noArgsConstructor;
|
private final MethodHandle noArgsConstructor;
|
||||||
private final Map<String, SubEntry> subEntries = new HashMap<>();
|
private final Map<String, SubEntry> subEntries = new LinkedHashMap<>();
|
||||||
private final Map<String, ConfigEntry<?>> subConfigEntries = new HashMap<>();
|
private final Map<String, ConfigEntry<?>> subConfigEntries = new LinkedHashMap<>();
|
||||||
|
|
||||||
public StaticPojoCompoundConfigEntry(@NotNull Class<T> valueClass, @NotNull MethodHandle noArgsConstructor) {
|
public StaticPojoCompoundConfigEntry(@NotNull Class<T> valueClass, @NotNull MethodHandle noArgsConstructor) {
|
||||||
super(valueClass);
|
super(valueClass);
|
||||||
@@ -67,7 +67,7 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
|
|||||||
public T instantiateCompoundValue() {
|
public T instantiateCompoundValue() {
|
||||||
try {
|
try {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (T) noArgsConstructor.invokeExact();
|
return (T) noArgsConstructor.invoke();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new IllegalStateException("Failed to instantiate compound class", e);
|
throw new IllegalStateException("Failed to instantiate compound class", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import java.lang.reflect.Field;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -46,7 +46,7 @@ public class PojoClassIntrospector {
|
|||||||
|
|
||||||
public Map<String, Property> properties() {
|
public Map<String, Property> properties() {
|
||||||
if (this.properties == null) {
|
if (this.properties == null) {
|
||||||
this.properties = new HashMap<>();
|
this.properties = new LinkedHashMap<>();
|
||||||
Class<?> currentClass = clazz;
|
Class<?> currentClass = clazz;
|
||||||
while (currentClass != null) {
|
while (currentClass != null) {
|
||||||
appendClassProperties(currentClass);
|
appendClassProperties(currentClass);
|
||||||
|
|||||||
@@ -8,27 +8,36 @@ import de.siphalor.tweed5.patchwork.api.PatchworkClassCreator;
|
|||||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClass;
|
import de.siphalor.tweed5.patchwork.impl.PatchworkClass;
|
||||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassGenerator;
|
import de.siphalor.tweed5.patchwork.impl.PatchworkClassGenerator;
|
||||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassPart;
|
import de.siphalor.tweed5.patchwork.impl.PatchworkClassPart;
|
||||||
|
import de.siphalor.tweed5.utils.api.collection.ClassToInstancesMultimap;
|
||||||
|
import de.siphalor.tweed5.utils.api.collection.InheritanceMap;
|
||||||
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.Annotations;
|
||||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver;
|
import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver;
|
||||||
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.*;
|
import lombok.*;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that sets up and handles all the bits and bobs for weaving a {@link ConfigContainer} out of a POJO.
|
* A class that sets up and handles all the bits and bobs for weaving a {@link ConfigContainer} out of a POJO.
|
||||||
* The POJO must be annotated with {@link PojoWeaving}.
|
* The POJO must be annotated with {@link PojoWeaving}.
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@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 ConfigContainer<T> configContainer;
|
private final ConfigContainer<T> configContainer;
|
||||||
private final Collection<TweedPojoWeaver> weavers;
|
private final Collection<TweedPojoWeaver> weavers;
|
||||||
|
private final Collection<TweedPojoWeavingPostProcessor> postProcessors;
|
||||||
private PatchworkClass<WeavingContext.ExtensionsData> contextExtensionsDataClass;
|
private PatchworkClass<WeavingContext.ExtensionsData> contextExtensionsDataClass;
|
||||||
|
|
||||||
public static <T> TweedPojoWeaverBootstrapper<T> create(Class<T> pojoClass) {
|
public static <T> TweedPojoWeaverBootstrapper<T> create(Class<T> pojoClass) {
|
||||||
@@ -37,11 +46,14 @@ public class TweedPojoWeaverBootstrapper<T> {
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
ConfigContainer<T> configContainer = (ConfigContainer<T>) createConfigContainer((Class<? extends ConfigContainer<?>>) rootWeavingConfig.container());
|
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()));
|
||||||
|
|
||||||
Collection<TweedExtension> extensions = loadExtensions(Arrays.asList(rootWeavingConfig.extensions()));
|
Collection<TweedExtension> extensions = loadExtensions(Arrays.asList(rootWeavingConfig.extensions()));
|
||||||
configContainer.registerExtensions(extensions.toArray(new TweedExtension[0]));
|
configContainer.registerExtensions(extensions.toArray(new TweedExtension[0]));
|
||||||
configContainer.finishExtensionSetup();
|
configContainer.finishExtensionSetup();
|
||||||
|
|
||||||
return new TweedPojoWeaverBootstrapper<>(pojoClass, configContainer, loadWeavers(Arrays.asList(rootWeavingConfig.weavers())));
|
return new TweedPojoWeaverBootstrapper<>(pojoClass, configContainer, weavers, postProcessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Collection<TweedExtension> loadExtensions(Collection<Class<? extends TweedExtension>> extensionClasses) {
|
private static Collection<TweedExtension> loadExtensions(Collection<Class<? extends TweedExtension>> extensionClasses) {
|
||||||
@@ -53,11 +65,15 @@ public class TweedPojoWeaverBootstrapper<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Collection<TweedPojoWeaver> loadWeavers(Collection<Class<? extends TweedPojoWeaver>> weaverClasses) {
|
private static Collection<TweedPojoWeaver> loadWeavers(Collection<Class<? extends TweedPojoWeaver>> weaverClasses) {
|
||||||
List<TweedPojoWeaver> weavers = new ArrayList<>();
|
return weaverClasses.stream()
|
||||||
for (Class<? extends TweedPojoWeaver> weaverClass : weaverClasses) {
|
.map(weaverClass -> checkImplementsAndInstantiate(TweedPojoWeaver.class, weaverClass))
|
||||||
weavers.add(checkImplementsAndInstantiate(TweedPojoWeaver.class, weaverClass));
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
return weavers;
|
|
||||||
|
private static Collection<TweedPojoWeavingPostProcessor> loadPostProcessors(Collection<Class<? extends TweedPojoWeavingPostProcessor>> postProcessorClasses) {
|
||||||
|
return postProcessorClasses.stream()
|
||||||
|
.map(postProcessorClass -> checkImplementsAndInstantiate(TweedPojoWeavingPostProcessor.class, postProcessorClass))
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigContainer<?> createConfigContainer(Class<? extends ConfigContainer<?>> containerClass) {
|
private static ConfigContainer<?> createConfigContainer(Class<? extends ConfigContainer<?>> containerClass) {
|
||||||
@@ -197,9 +213,13 @@ public class TweedPojoWeaverBootstrapper<T> {
|
|||||||
private WeavingContext createWeavingContext() {
|
private WeavingContext createWeavingContext() {
|
||||||
try {
|
try {
|
||||||
WeavingContext.ExtensionsData extensionsData = (WeavingContext.ExtensionsData) contextExtensionsDataClass.constructor().invoke();
|
WeavingContext.ExtensionsData extensionsData = (WeavingContext.ExtensionsData) contextExtensionsDataClass.constructor().invoke();
|
||||||
return WeavingContext.builder()
|
|
||||||
|
Annotations annotations = new Annotations();
|
||||||
|
annotations.addAnnotationsFrom(ElementType.TYPE, pojoClass);
|
||||||
|
|
||||||
|
return WeavingContext.builder(this::weaveEntry, configContainer)
|
||||||
.extensionsData(extensionsData)
|
.extensionsData(extensionsData)
|
||||||
.weavingFunction(this::weaveEntry)
|
.annotations(annotations)
|
||||||
.build();
|
.build();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new PojoWeavingException("Failed to create weaving context's extension data");
|
throw new PojoWeavingException("Failed to create weaving context's extension data");
|
||||||
@@ -210,7 +230,10 @@ public class TweedPojoWeaverBootstrapper<T> {
|
|||||||
for (TweedPojoWeaver weaver : weavers) {
|
for (TweedPojoWeaver weaver : weavers) {
|
||||||
ConfigEntry<U> configEntry = weaver.weaveEntry(dataClass, context);
|
ConfigEntry<U> configEntry = weaver.weaveEntry(dataClass, context);
|
||||||
if (configEntry != null) {
|
if (configEntry != null) {
|
||||||
configEntry.seal(configContainer);
|
if (!configEntry.sealed()) {
|
||||||
|
configEntry.seal(configContainer);
|
||||||
|
}
|
||||||
|
applyPostProcessors(configEntry, context);
|
||||||
return configEntry;
|
return configEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,6 +241,16 @@ public class TweedPojoWeaverBootstrapper<T> {
|
|||||||
throw new PojoWeavingException("Failed to weave " + dataClass.getName() + ": No matching weavers found");
|
throw new PojoWeavingException("Failed to weave " + dataClass.getName() + ": No matching weavers found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyPostProcessors(ConfigEntry<?> configEntry, WeavingContext context) {
|
||||||
|
for (TweedPojoWeavingPostProcessor postProcessor : postProcessors) {
|
||||||
|
try {
|
||||||
|
postProcessor.apply(configEntry, context);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to apply Tweed POJO weaver post processor", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private static class RegisteredExtensionDataImpl<E> implements RegisteredExtensionData<WeavingContext.ExtensionsData, E> {
|
private static class RegisteredExtensionDataImpl<E> implements RegisteredExtensionData<WeavingContext.ExtensionsData, E> {
|
||||||
private MethodHandle setter;
|
private MethodHandle setter;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
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.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||||
@@ -12,6 +13,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
|
||||||
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;
|
||||||
@@ -30,9 +33,10 @@ class CompoundPojoWeaverTest {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
WeavingContext weavingContext = WeavingContext.builder()
|
Annotations annotations = new Annotations();
|
||||||
.extensionsData(new ExtensionsDataMock(null))
|
annotations.addAnnotationsFrom(ElementType.TYPE, Compound.class);
|
||||||
.weavingFunction(new TweedPojoWeavingFunction.NonNull() {
|
|
||||||
|
WeavingContext weavingContext = WeavingContext.builder(new TweedPojoWeavingFunction.NonNull() {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull <T> ConfigEntry<T> weaveEntry(Class<T> valueClass, WeavingContext context) {
|
public @NotNull <T> ConfigEntry<T> weaveEntry(Class<T> valueClass, WeavingContext context) {
|
||||||
ConfigEntry<T> entry = compoundWeaver.weaveEntry(valueClass, context);
|
ConfigEntry<T> entry = compoundWeaver.weaveEntry(valueClass, context);
|
||||||
@@ -45,7 +49,9 @@ class CompoundPojoWeaverTest {
|
|||||||
return configEntry;
|
return configEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}, mock(ConfigContainer.class))
|
||||||
|
.extensionsData(new ExtensionsDataMock(null))
|
||||||
|
.annotations(annotations)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ConfigEntry<Compound> resultEntry = compoundWeaver.weaveEntry(Compound.class, weavingContext);
|
ConfigEntry<Compound> resultEntry = compoundWeaver.weaveEntry(Compound.class, weavingContext);
|
||||||
|
|||||||
Reference in New Issue
Block a user