Initial commit
That's a lotta stuff for an initial commit, but well...
This commit is contained in:
6
tweed5-default-extensions/build.gradle.kts
Normal file
6
tweed5-default-extensions/build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
||||
dependencies {
|
||||
api(project(":tweed5-core"))
|
||||
api(project(":tweed5-serde-extension"))
|
||||
|
||||
testImplementation(project(":tweed5-serde-hjson"))
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.api;
|
||||
|
||||
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.FIELD)
|
||||
public @interface AComment {
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
|
||||
public interface CommentModifyingExtension {
|
||||
Middleware<CommentProducer> commentMiddleware();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CommentProducer {
|
||||
String createComment(ConfigEntry<?> entry);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.AComment;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CommentExtension implements TweedExtension, ReadWriteRelatedExtension {
|
||||
@Getter
|
||||
private RegisteredExtensionData<EntryExtensionsData, InternalCommentEntryData> internalEntryDataExtension;
|
||||
private DefaultMiddlewareContainer<CommentProducer> middlewareContainer;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "comment";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup(TweedExtensionSetupContext context) {
|
||||
internalEntryDataExtension = context.registerEntryExtensionData(InternalCommentEntryData.class);
|
||||
context.registerEntryExtensionData(AComment.class);
|
||||
|
||||
middlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
|
||||
for (TweedExtension extension : context.configContainer().extensions()) {
|
||||
if (extension instanceof CommentModifyingExtension) {
|
||||
middlewareContainer.register(((CommentModifyingExtension) extension).commentMiddleware());
|
||||
}
|
||||
}
|
||||
|
||||
middlewareContainer.seal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Middleware<TweedEntryWriter<?, ?>> entryWriterMiddleware() {
|
||||
return TweedEntryWriterCommentMiddleware.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initEntry(ConfigEntry<?> configEntry) {
|
||||
EntryExtensionsData entryExtensionsData = configEntry.extensionsData();
|
||||
String baseComment;
|
||||
if (entryExtensionsData.isPatchworkPartSet(AComment.class)) {
|
||||
baseComment = ((AComment) entryExtensionsData).value();
|
||||
} else {
|
||||
baseComment = "";
|
||||
}
|
||||
|
||||
CommentProducer middleware = middlewareContainer.process(entry -> baseComment);
|
||||
internalEntryDataExtension.set(entryExtensionsData, new InternalCommentEntryDataImpl(middleware));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getComment(ConfigEntry<?> configEntry) {
|
||||
String comment = ((InternalCommentEntryData) configEntry.extensionsData()).commentProducer().createComment(configEntry);
|
||||
return comment.isEmpty() ? null : comment;
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class InternalCommentEntryDataImpl implements InternalCommentEntryData {
|
||||
CommentProducer commentProducer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.impl;
|
||||
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
|
||||
public interface InternalCommentEntryData {
|
||||
CommentProducer commentProducer();
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?, ?>> {
|
||||
public static final TweedEntryWriterCommentMiddleware INSTANCE = new TweedEntryWriterCommentMiddleware();
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return "comment-writer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
|
||||
//noinspection unchecked
|
||||
TweedEntryWriter<Object, ConfigEntry<Object>> innerCasted = (TweedEntryWriter<Object, ConfigEntry<Object>>) inner;
|
||||
return (TweedEntryWriter<Object, ConfigEntry<Object>>) (writer, value, entry, context) -> {
|
||||
if (writer instanceof CompoundDataWriter) {
|
||||
// Comment is already written in front of the key by the CompoundDataWriter,
|
||||
// so we don't have to write it here.
|
||||
// We also want to unwrap the original writer,
|
||||
// so that the special comment writing is limited to compounds.
|
||||
writer = ((CompoundDataWriter) writer).delegate;
|
||||
} else {
|
||||
String comment = getEntryComment(entry);
|
||||
if (comment != null) {
|
||||
writer.visitComment(comment);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry instanceof CompoundConfigEntry) {
|
||||
innerCasted.write(
|
||||
new CompoundDataWriter(writer, ((CompoundConfigEntry<?>) entry)),
|
||||
value,
|
||||
entry,
|
||||
context
|
||||
);
|
||||
} else {
|
||||
innerCasted.write(writer, value, entry, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class CompoundDataWriter implements TweedDataWriter {
|
||||
private final TweedDataWriter delegate;
|
||||
private final CompoundConfigEntry<?> compoundConfigEntry;
|
||||
|
||||
@Override
|
||||
public void visitNull() {
|
||||
delegate.visitNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBoolean(boolean value) {
|
||||
delegate.visitBoolean(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitByte(byte value) {
|
||||
delegate.visitByte(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitShort(short value) {
|
||||
delegate.visitShort(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInt(int value) {
|
||||
delegate.visitInt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLong(long value) {
|
||||
delegate.visitLong(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFloat(float value) {
|
||||
delegate.visitFloat(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDouble(double value) {
|
||||
delegate.visitDouble(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitString(@NotNull String value) {
|
||||
delegate.visitString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListStart() {
|
||||
delegate.visitListStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListEnd() {
|
||||
delegate.visitListEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapStart() {
|
||||
delegate.visitMapStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEntryKey(String key) {
|
||||
ConfigEntry<?> subEntry = compoundConfigEntry.subEntries().get(key);
|
||||
String subEntryComment = getEntryComment(subEntry);
|
||||
if (subEntryComment != null) {
|
||||
delegate.visitComment(subEntryComment);
|
||||
}
|
||||
delegate.visitMapEntryKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEnd() {
|
||||
delegate.visitMapEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitComment(String comment) {
|
||||
delegate.visitComment(comment);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String getEntryComment(ConfigEntry<?> entry) {
|
||||
if (!entry.extensionsData().isPatchworkPartSet(InternalCommentEntryData.class)) {
|
||||
return null;
|
||||
}
|
||||
String comment = ((InternalCommentEntryData) entry.extensionsData()).commentProducer().createComment(entry).trim();
|
||||
return comment.isEmpty() ? null : comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@ApiStatus.Internal
|
||||
package de.siphalor.tweed5.defaultextensions.comment.impl;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -0,0 +1,170 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.EntryReaderWriterDefinition;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters;
|
||||
import de.siphalor.tweed5.data.extension.impl.ReadWriteExtensionImpl;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.AComment;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CommentExtensionTest {
|
||||
|
||||
private DefaultConfigContainer<Map<String, Object>> configContainer;
|
||||
private CommentExtension commentExtension;
|
||||
private StaticMapCompoundConfigEntryImpl<Map<String, Object>> rootEntry;
|
||||
private SimpleConfigEntryImpl<Integer> intEntry;
|
||||
private SimpleConfigEntryImpl<String> stringEntry;
|
||||
private SimpleConfigEntryImpl<Long> noCommentEntry;
|
||||
|
||||
void setupContainer(Collection<TweedExtension> extraExtensions) {
|
||||
configContainer = new DefaultConfigContainer<>();
|
||||
|
||||
commentExtension = new CommentExtension();
|
||||
configContainer.registerExtension(commentExtension);
|
||||
extraExtensions.forEach(configContainer::registerExtension);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
//noinspection unchecked
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(((Class<Map<String, Object>>)(Class<?>) Map.class), LinkedHashMap::new);
|
||||
|
||||
intEntry = new SimpleConfigEntryImpl<>(Integer.class);
|
||||
rootEntry.addSubEntry("int", intEntry);
|
||||
stringEntry = new SimpleConfigEntryImpl<>(String.class);
|
||||
rootEntry.addSubEntry("string", stringEntry);
|
||||
noCommentEntry = new SimpleConfigEntryImpl<>(Long.class);
|
||||
rootEntry.addSubEntry("noComment", noCommentEntry);
|
||||
|
||||
configContainer.attachAndSealTree(rootEntry);
|
||||
|
||||
//noinspection unchecked
|
||||
RegisteredExtensionData<EntryExtensionsData, AComment> commentData = (RegisteredExtensionData<EntryExtensionsData, AComment>) configContainer.entryDataExtensions().get(AComment.class);
|
||||
|
||||
commentData.set(rootEntry.extensionsData(), new ACommentImpl("This is the root value.\nIt is the topmost value in the tree."));
|
||||
commentData.set(intEntry.extensionsData(), new ACommentImpl("It is an integer"));
|
||||
commentData.set(stringEntry.extensionsData(), new ACommentImpl("It is a string"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleComments() {
|
||||
setupContainer(Collections.emptyList());
|
||||
configContainer.initialize();
|
||||
|
||||
assertEquals("It is an integer", commentExtension.getComment(intEntry));
|
||||
assertEquals("It is a string", commentExtension.getComment(stringEntry));
|
||||
assertNull(commentExtension.getComment(noCommentEntry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void commentProvidingExtension() {
|
||||
setupContainer(Collections.singletonList(new TestCommentModifyingExtension()));
|
||||
configContainer.initialize();
|
||||
|
||||
assertEquals("The comment is:\nIt is an integer\nEND", commentExtension.getComment(intEntry));
|
||||
assertEquals("The comment is:\nIt is a string\nEND", commentExtension.getComment(stringEntry));
|
||||
assertEquals("The comment is:\n\nEND", commentExtension.getComment(noCommentEntry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleCommentsInHjson() {
|
||||
ReadWriteExtension readWriteExtension = new ReadWriteExtensionImpl();
|
||||
setupContainer(Collections.singletonList(readWriteExtension));
|
||||
setupReadWriteTypes();
|
||||
configContainer.initialize();
|
||||
|
||||
Map<String, Object> value = new HashMap<>();
|
||||
value.put("int", 123);
|
||||
value.put("string", "Hello World");
|
||||
value.put("noComment", 567L);
|
||||
|
||||
StringWriter output = new StringWriter();
|
||||
assertDoesNotThrow(() -> readWriteExtension.write(
|
||||
new HjsonWriter(output, new HjsonWriter.Options().multilineCommentType(HjsonCommentType.SLASHES)),
|
||||
value,
|
||||
rootEntry,
|
||||
readWriteExtension.createReadWriteContextExtensionsData()
|
||||
));
|
||||
|
||||
assertEquals("// This is the root value.\n// It is the topmost value in the tree.\n" +
|
||||
"{\n\t// It is an integer\n\tint: 123\n\t// It is a string\n" +
|
||||
"\tstring: Hello World\n\tnoComment: 567\n}\n", output.toString());
|
||||
}
|
||||
|
||||
private void setupReadWriteTypes() {
|
||||
//noinspection unchecked
|
||||
RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition> readerWriterData = (RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition>) configContainer.entryDataExtensions().get(EntryReaderWriterDefinition.class);
|
||||
|
||||
readerWriterData.set(rootEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.compoundReaderWriter()));
|
||||
readerWriterData.set(intEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.intReaderWriter()));
|
||||
readerWriterData.set(stringEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.stringReaderWriter()));
|
||||
readerWriterData.set(noCommentEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.longReaderWriter()));
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class ACommentImpl implements AComment {
|
||||
String value;
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestCommentModifyingExtension implements TweedExtension, CommentModifyingExtension {
|
||||
@Override
|
||||
public String getId() {
|
||||
return "test-extension";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Middleware<CommentProducer> commentMiddleware() {
|
||||
return new Middleware<CommentProducer>() {
|
||||
@Override
|
||||
public String id() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommentProducer process(CommentProducer inner) {
|
||||
return entry -> "The comment is:\n" + inner.createComment(entry) + "\nEND";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class TrivialEntryReaderWriterDefinition implements EntryReaderWriterDefinition {
|
||||
private final TweedEntryReaderWriter<?, ?> readerWriter;
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> reader() {
|
||||
return readerWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> writer() {
|
||||
return readerWriter;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user