From 7ce3aaac06cb1486123722c95fa65437b9af8588 Mon Sep 17 00:00:00 2001 From: Siphalor Date: Sun, 3 Aug 2025 20:36:28 +0200 Subject: [PATCH] [serde-*] Make data readers and writers AutoCloseable --- .../testutils/serde/json/JsonReaderTest.java | 98 ++++++++++--------- ...ttributesReadWriteFilterExtensionImpl.java | 7 +- .../TweedEntryWriterCommentMiddleware.java | 5 +- .../pather/api/PathTrackingDataReader.java | 5 + .../pather/impl/PatherExtensionImpl.java | 1 + ...or.java => DelegatingTweedDataWriter.java} | 9 +- .../tweed5/dataapi/api/TweedDataReader.java | 2 +- .../tweed5/dataapi/api/TweedDataWriter.java | 4 + .../data/extension/api/TweedEntryWriter.java | 2 +- .../tweed5/data/gson/GsonReader.java | 5 + .../tweed5/data/gson/GsonWriter.java | 8 +- .../tweed5/data/hjson/HjsonLexer.java | 7 +- .../tweed5/data/hjson/HjsonReader.java | 5 + .../tweed5/data/hjson/HjsonWriter.java | 9 +- .../tweed5/data/jackson/JacksonReader.java | 5 + .../tweed5/data/jackson/JacksonWriter.java | 8 +- 16 files changed, 117 insertions(+), 63 deletions(-) rename tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/{DelegatingTweedDataVisitor.java => DelegatingTweedDataWriter.java} (91%) create mode 100644 tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataWriter.java diff --git a/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java b/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java index 925f2af..5a152f0 100644 --- a/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java +++ b/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java @@ -12,7 +12,7 @@ public interface JsonReaderTest { @Test @SneakyThrows default void complexJsonReadTest() { - var reader = createJsonReader(""" + String text = """ { "first": [ [ 1 ] @@ -21,53 +21,55 @@ public interface JsonReaderTest { "test": "Hello World!" } } - """); + """; - var token = reader.peekToken(); - assertThat(token.isMapStart()).isTrue(); - token = reader.readToken(); - assertThat(token.isMapStart()).isTrue(); - token = reader.readToken(); - assertThat(token.isMapEntryKey()).isTrue(); - assertThat(token.canReadAsString()).isTrue(); - assertThat(token.readAsString()).isEqualTo("first"); - token = reader.readToken(); - assertThat(token.isMapEntryValue()).isTrue(); - assertThat(token.isListStart()).isTrue(); - token = reader.readToken(); - assertThat(token.isMapEntryValue()).isFalse(); - assertThat(token.isListValue()).isTrue(); - assertThat(token.isListStart()).isTrue(); - token = reader.readToken(); - assertThat(token.isListValue()).isTrue(); - assertThat(token.canReadAsInt()).isTrue(); - assertThat(token.readAsInt()).isEqualTo(1); - token = reader.readToken(); - assertThat(token.isListValue()).isTrue(); - assertThat(token.isListEnd()).isTrue(); - token = reader.readToken(); - assertThat(token.isListValue()).isFalse(); - assertThat(token.isMapEntryValue()).isTrue(); - assertThat(token.isListEnd()).isTrue(); - token = reader.readToken(); - assertThat(token.isMapEntryKey()).isTrue(); - assertThat(token.canReadAsString()).isTrue(); - assertThat(token.readAsString()).isEqualTo("second"); - token = reader.readToken(); - assertThat(token.isMapEntryValue()).isTrue(); - assertThat(token.isMapStart()).isTrue(); - token = reader.readToken(); - assertThat(token.isMapEntryValue()).isFalse(); - assertThat(token.isMapEntryKey()).isTrue(); - assertThat(token.canReadAsString()).isTrue(); - assertThat(token.readAsString()).isEqualTo("test"); - token = reader.readToken(); - assertThat(token.isMapEntryValue()).isTrue(); - assertThat(token.canReadAsString()).isTrue(); - assertThat(token.readAsString()).isEqualTo("Hello World!"); - token = reader.readToken(); - assertThat(token.isMapEnd()).isTrue(); - token = reader.readToken(); - assertThat(token.isMapEnd()).isTrue(); + try (var reader = createJsonReader(text)) { + var token = reader.peekToken(); + assertThat(token.isMapStart()).isTrue(); + token = reader.readToken(); + assertThat(token.isMapStart()).isTrue(); + token = reader.readToken(); + assertThat(token.isMapEntryKey()).isTrue(); + assertThat(token.canReadAsString()).isTrue(); + assertThat(token.readAsString()).isEqualTo("first"); + token = reader.readToken(); + assertThat(token.isMapEntryValue()).isTrue(); + assertThat(token.isListStart()).isTrue(); + token = reader.readToken(); + assertThat(token.isMapEntryValue()).isFalse(); + assertThat(token.isListValue()).isTrue(); + assertThat(token.isListStart()).isTrue(); + token = reader.readToken(); + assertThat(token.isListValue()).isTrue(); + assertThat(token.canReadAsInt()).isTrue(); + assertThat(token.readAsInt()).isEqualTo(1); + token = reader.readToken(); + assertThat(token.isListValue()).isTrue(); + assertThat(token.isListEnd()).isTrue(); + token = reader.readToken(); + assertThat(token.isListValue()).isFalse(); + assertThat(token.isMapEntryValue()).isTrue(); + assertThat(token.isListEnd()).isTrue(); + token = reader.readToken(); + assertThat(token.isMapEntryKey()).isTrue(); + assertThat(token.canReadAsString()).isTrue(); + assertThat(token.readAsString()).isEqualTo("second"); + token = reader.readToken(); + assertThat(token.isMapEntryValue()).isTrue(); + assertThat(token.isMapStart()).isTrue(); + token = reader.readToken(); + assertThat(token.isMapEntryValue()).isFalse(); + assertThat(token.isMapEntryKey()).isTrue(); + assertThat(token.canReadAsString()).isTrue(); + assertThat(token.readAsString()).isEqualTo("test"); + token = reader.readToken(); + assertThat(token.isMapEntryValue()).isTrue(); + assertThat(token.canReadAsString()).isTrue(); + assertThat(token.readAsString()).isEqualTo("Hello World!"); + token = reader.readToken(); + assertThat(token.isMapEnd()).isTrue(); + token = reader.readToken(); + assertThat(token.isMapEnd()).isTrue(); + } } } diff --git a/tweed5-attributes-extension/src/main/java/de/siphalor/tweed5/attributesextension/impl/serde/filter/AttributesReadWriteFilterExtensionImpl.java b/tweed5-attributes-extension/src/main/java/de/siphalor/tweed5/attributesextension/impl/serde/filter/AttributesReadWriteFilterExtensionImpl.java index 7a48ebf..12710c0 100644 --- a/tweed5-attributes-extension/src/main/java/de/siphalor/tweed5/attributesextension/impl/serde/filter/AttributesReadWriteFilterExtensionImpl.java +++ b/tweed5-attributes-extension/src/main/java/de/siphalor/tweed5/attributesextension/impl/serde/filter/AttributesReadWriteFilterExtensionImpl.java @@ -16,10 +16,7 @@ import de.siphalor.tweed5.data.extension.api.TweedReadContext; import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext; import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; -import de.siphalor.tweed5.dataapi.api.DelegatingTweedDataVisitor; -import de.siphalor.tweed5.dataapi.api.TweedDataReader; -import de.siphalor.tweed5.dataapi.api.TweedDataUnsupportedValueException; -import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; +import de.siphalor.tweed5.dataapi.api.*; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration; import de.siphalor.tweed5.patchwork.api.Patchwork; import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; @@ -285,7 +282,7 @@ public class AttributesReadWriteFilterExtensionImpl return true; } - private static class MapEntryKeyDeferringWriter extends DelegatingTweedDataVisitor { + private static class MapEntryKeyDeferringWriter extends DelegatingTweedDataWriter { private final Deque mapContext = new ArrayDeque<>(); private final Deque preDecorationQueue = new ArrayDeque<>(); private final Deque postDecorationQueue = new ArrayDeque<>(); diff --git a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/comment/impl/TweedEntryWriterCommentMiddleware.java b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/comment/impl/TweedEntryWriterCommentMiddleware.java index e70b3d6..ffc8641 100644 --- a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/comment/impl/TweedEntryWriterCommentMiddleware.java +++ b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/comment/impl/TweedEntryWriterCommentMiddleware.java @@ -3,8 +3,9 @@ package de.siphalor.tweed5.defaultextensions.comment.impl; 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.DelegatingTweedDataVisitor; +import de.siphalor.tweed5.dataapi.api.DelegatingTweedDataWriter; import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; +import de.siphalor.tweed5.dataapi.api.TweedDataWriter; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration; import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; @@ -48,7 +49,7 @@ class TweedEntryWriterCommentMiddleware implements Middleware decorationQueue = new ArrayDeque<>(); private @Nullable String mapEntryKey; diff --git a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReader.java b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReader.java index 01e0fed..c3e20ae 100644 --- a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReader.java +++ b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReader.java @@ -36,4 +36,9 @@ public class PathTrackingDataReader implements TweedDataReader { } return token; } + + @Override + public void close() throws Exception { + delegate.close(); + } } diff --git a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/impl/PatherExtensionImpl.java b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/impl/PatherExtensionImpl.java index e64f379..20a7436 100644 --- a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/impl/PatherExtensionImpl.java +++ b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/pather/impl/PatherExtensionImpl.java @@ -6,6 +6,7 @@ import de.siphalor.tweed5.core.api.middleware.Middleware; import de.siphalor.tweed5.data.extension.api.*; import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext; import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension; +import de.siphalor.tweed5.dataapi.api.DelegatingTweedDataWriter; import de.siphalor.tweed5.dataapi.api.TweedDataReader; import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking; diff --git a/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/DelegatingTweedDataVisitor.java b/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/DelegatingTweedDataWriter.java similarity index 91% rename from tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/DelegatingTweedDataVisitor.java rename to tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/DelegatingTweedDataWriter.java index eed38d8..bc6ad57 100644 --- a/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/DelegatingTweedDataVisitor.java +++ b/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/DelegatingTweedDataWriter.java @@ -8,7 +8,7 @@ import org.jspecify.annotations.Nullable; @Getter @RequiredArgsConstructor(access = AccessLevel.PROTECTED) -public class DelegatingTweedDataVisitor implements TweedDataVisitor { +public class DelegatingTweedDataWriter implements TweedDataWriter { protected final TweedDataVisitor delegate; @Override @@ -115,4 +115,11 @@ public class DelegatingTweedDataVisitor implements TweedDataVisitor { public void visitDecoration(TweedDataDecoration decoration) { delegate.visitDecoration(decoration); } + + @Override + public void close() throws Exception { + if (delegate instanceof AutoCloseable) { + ((AutoCloseable) delegate).close(); + } + } } diff --git a/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataReader.java b/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataReader.java index b92843b..95eae9e 100644 --- a/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataReader.java +++ b/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataReader.java @@ -1,6 +1,6 @@ package de.siphalor.tweed5.dataapi.api; -public interface TweedDataReader { +public interface TweedDataReader extends AutoCloseable { TweedDataToken peekToken() throws TweedDataReadException; TweedDataToken readToken() throws TweedDataReadException; } diff --git a/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataWriter.java b/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataWriter.java new file mode 100644 index 0000000..9ded985 --- /dev/null +++ b/tweed5-serde-api/src/main/java/de/siphalor/tweed5/dataapi/api/TweedDataWriter.java @@ -0,0 +1,4 @@ +package de.siphalor.tweed5.dataapi.api; + +public interface TweedDataWriter extends TweedDataVisitor, AutoCloseable { +} diff --git a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriter.java b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriter.java index b5d33b1..76b1cbb 100644 --- a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriter.java +++ b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriter.java @@ -1,8 +1,8 @@ package de.siphalor.tweed5.data.extension.api; import de.siphalor.tweed5.core.api.entry.ConfigEntry; -import de.siphalor.tweed5.dataapi.api.TweedDataWriteException; import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; +import de.siphalor.tweed5.dataapi.api.TweedDataWriteException; import org.jspecify.annotations.Nullable; @FunctionalInterface diff --git a/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonReader.java b/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonReader.java index 4ab7093..385de30 100644 --- a/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonReader.java +++ b/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonReader.java @@ -266,6 +266,11 @@ public class GsonReader implements TweedDataReader { }; } + @Override + public void close() throws Exception { + reader.close(); + } + private enum Context { VALUE, LIST, diff --git a/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonWriter.java b/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonWriter.java index b31dc73..c9d34c8 100644 --- a/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonWriter.java +++ b/tweed5-serde-gson/src/main/java/de/siphaolor/tweed5/data/gson/GsonWriter.java @@ -3,6 +3,7 @@ package de.siphaolor.tweed5.data.gson; import com.google.gson.stream.JsonWriter; import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; import de.siphalor.tweed5.dataapi.api.TweedDataWriteException; +import de.siphalor.tweed5.dataapi.api.TweedDataWriter; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration; import org.jspecify.annotations.Nullable; @@ -11,7 +12,7 @@ import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; -public class GsonWriter implements TweedDataVisitor { +public class GsonWriter implements TweedDataWriter { private final JsonWriter writer; private final Deque contextStack = new ArrayDeque<>(); @@ -181,6 +182,11 @@ public class GsonWriter implements TweedDataVisitor { } } + @Override + public void close() throws Exception { + writer.close(); + } + private void afterValueWritten() { if (peekContext() == Context.VALUE) { contextStack.pop(); diff --git a/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonLexer.java b/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonLexer.java index 30da99e..6feccf6 100644 --- a/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonLexer.java +++ b/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonLexer.java @@ -11,7 +11,7 @@ import java.util.PrimitiveIterator; @ApiStatus.Internal @RequiredArgsConstructor -public class HjsonLexer { +public class HjsonLexer implements AutoCloseable { private static final int EMPTY_CODEPOINT = -2; private final Reader reader; @@ -482,4 +482,9 @@ public class HjsonLexer { throw TweedDataReadException.builder().message("Failed to read character from input at " + currentPos).cause(e).build(); } } + + @Override + public void close() throws Exception { + reader.close(); + } } diff --git a/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonReader.java b/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonReader.java index be510e5..0e8fc44 100644 --- a/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonReader.java +++ b/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonReader.java @@ -630,6 +630,11 @@ public class HjsonReader implements TweedDataReader { return contexts.peek(); } + @Override + public void close() throws Exception { + lexer.close(); + } + private enum Context { VALUE, LIST, diff --git a/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonWriter.java b/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonWriter.java index 9a49375..e0a995e 100644 --- a/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonWriter.java +++ b/tweed5-serde-hjson/src/main/java/de/siphalor/tweed5/data/hjson/HjsonWriter.java @@ -1,7 +1,7 @@ package de.siphalor.tweed5.data.hjson; import de.siphalor.tweed5.dataapi.api.TweedDataWriteException; -import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; +import de.siphalor.tweed5.dataapi.api.TweedDataWriter; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration; import lombok.Data; @@ -12,7 +12,7 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class HjsonWriter implements TweedDataVisitor { +public class HjsonWriter implements TweedDataWriter { private static final int PREFILL_INDENT = 10; private static final Pattern LINE_FEED_PATTERN = Pattern.compile("\\n|\\r\\n"); private static final Pattern NUMBER_PATTERN = Pattern.compile("^-?\\d+(?:\\.\\d*)?(?:[eE][+-]?\\d+)?$"); @@ -580,6 +580,11 @@ public class HjsonWriter implements TweedDataVisitor { return new TweedDataWriteException("Writing Hjson failed", e); } + @Override + public void close() throws Exception { + writer.close(); + } + private enum Context { ROOT, LIST, diff --git a/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java b/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java index 0c277db..8a69438 100644 --- a/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java +++ b/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java @@ -256,6 +256,11 @@ public class JacksonReader implements TweedDataReader { }; } + @Override + public void close() throws Exception { + parser.close(); + } + private enum Context { VALUE, LIST, diff --git a/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonWriter.java b/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonWriter.java index f045738..7acdcaf 100644 --- a/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonWriter.java +++ b/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonWriter.java @@ -3,6 +3,7 @@ package de.siphalor.tweed5.data.jackson; import com.fasterxml.jackson.core.JsonGenerator; import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; import de.siphalor.tweed5.dataapi.api.TweedDataWriteException; +import de.siphalor.tweed5.dataapi.api.TweedDataWriter; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration; import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration; import org.jspecify.annotations.Nullable; @@ -11,7 +12,7 @@ import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; -public class JacksonWriter implements TweedDataVisitor { +public class JacksonWriter implements TweedDataWriter { private final JsonGenerator generator; private final CommentWriteMode commentWriteMode; @@ -198,6 +199,11 @@ public class JacksonWriter implements TweedDataVisitor { } } + @Override + public void close() throws Exception { + generator.close(); + } + private void afterValueVisited() { if (contextStack.peek() == Context.VALUE) { contextStack.pop();