Initial commit

That's a lotta stuff for an initial commit, but well...
This commit is contained in:
2024-05-25 19:22:26 +02:00
commit b0f35b03b9
99 changed files with 6476 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
package de.siphalor.tweed5.defaultextensions.comment.impl;
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
public interface InternalCommentEntryData {
CommentProducer commentProducer();
}

View File

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

View File

@@ -0,0 +1,4 @@
@ApiStatus.Internal
package de.siphalor.tweed5.defaultextensions.comment.impl;
import org.jetbrains.annotations.ApiStatus;

View File

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