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 4a30b3b..40eb42e 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 @@ -4,10 +4,7 @@ import com.google.auto.service.AutoService; import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.api.extension.TweedExtension; import de.siphalor.tweed5.core.api.middleware.Middleware; -import de.siphalor.tweed5.data.extension.api.TweedEntryReader; -import de.siphalor.tweed5.data.extension.api.TweedEntryWriter; -import de.siphalor.tweed5.data.extension.api.TweedReadContext; -import de.siphalor.tweed5.data.extension.api.TweedWriteContext; +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.TweedDataReader; @@ -18,6 +15,7 @@ import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingDataVisitor; import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension; import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; import lombok.val; +import lombok.var; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -84,7 +82,21 @@ public class PatherExtensionImpl implements PatherExtension, TweedExtension, Rea pathTracking = new PathTracking(); context.extensionsData().set(rwContextPathTrackingAccess, pathTracking); - return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context); + try { + return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context); + } catch (TweedEntryReadException e) { + var exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess); + if (exceptionPathTracking != null) { + throw new TweedEntryReadException( + "Exception while reading entry at " + + String.join("/", exceptionPathTracking.currentPath()) + + ": " + e.getMessage(), + e + ); + } else { + throw e; + } + } }; } }; @@ -113,7 +125,21 @@ public class PatherExtensionImpl implements PatherExtension, TweedExtension, Rea pathTracking = new PathTracking(); context.extensionsData().set(rwContextPathTrackingAccess, pathTracking); - castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context); + try { + castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context); + } catch (TweedEntryWriteException e) { + var exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess); + if (exceptionPathTracking != null) { + throw new TweedEntryWriteException( + "Exception while writing entry at " + + String.join("/", exceptionPathTracking.currentPath()) + + ": " + e.getMessage(), + e + ); + } else { + throw e; + } + } }; } }; diff --git a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImpl.java b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImpl.java index a575496..8628a78 100644 --- a/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImpl.java +++ b/tweed5-default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImpl.java @@ -249,7 +249,10 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid } if (validationResult.hasError()) { - throw new TweedEntryReadException("Failed to validate entry: " + validationResult.issues()); + throw new TweedEntryReadException( + "Failed to validate entry: " + validationResult.issues(), + context + ); } return validationResult.value(); diff --git a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReadException.java b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReadException.java index b55a5e5..655e58f 100644 --- a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReadException.java +++ b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReadException.java @@ -1,18 +1,28 @@ package de.siphalor.tweed5.data.extension.api; +import lombok.Getter; + +@Getter public class TweedEntryReadException extends Exception { - public TweedEntryReadException() { - } + private final TweedReadContext context; - public TweedEntryReadException(String message) { + public TweedEntryReadException(String message, TweedReadContext context) { super(message); + this.context = context; } - public TweedEntryReadException(String message, Throwable cause) { + public TweedEntryReadException(String message, Throwable cause, TweedReadContext context) { super(message, cause); + this.context = context; } - public TweedEntryReadException(Throwable cause) { + public TweedEntryReadException(String message, TweedEntryReadException cause) { + super(message, cause); + this.context = cause.context; + } + + public TweedEntryReadException(Throwable cause, TweedReadContext context) { super(cause); + this.context = context; } } diff --git a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReader.java b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReader.java index 2c2ec4d..b29c4a7 100644 --- a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReader.java +++ b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryReader.java @@ -1,13 +1,10 @@ package de.siphalor.tweed5.data.extension.api; import de.siphalor.tweed5.core.api.entry.ConfigEntry; -import de.siphalor.tweed5.dataapi.api.TweedDataReadException; import de.siphalor.tweed5.dataapi.api.TweedDataReader; import org.jspecify.annotations.Nullable; @FunctionalInterface public interface TweedEntryReader> { - T read(TweedDataReader reader, C entry, TweedReadContext context) throws - TweedEntryReadException, - TweedDataReadException; + T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException; } diff --git a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriteException.java b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriteException.java index a1d85e5..ddee64b 100644 --- a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriteException.java +++ b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/TweedEntryWriteException.java @@ -1,18 +1,28 @@ package de.siphalor.tweed5.data.extension.api; +import lombok.Getter; + +@Getter public class TweedEntryWriteException extends Exception { - public TweedEntryWriteException() { - } + private final TweedWriteContext context; - public TweedEntryWriteException(String message) { + public TweedEntryWriteException(String message, TweedWriteContext context) { super(message); + this.context = context; } - public TweedEntryWriteException(String message, Throwable cause) { + public TweedEntryWriteException(String message, Throwable cause, TweedWriteContext context) { super(message, cause); + this.context = context; } - public TweedEntryWriteException(Throwable cause) { + public TweedEntryWriteException(String message, TweedEntryWriteException cause) { + super(message, cause); + this.context = cause.context; + } + + public TweedEntryWriteException(Throwable cause, TweedWriteContext context) { super(cause); + this.context = context; } } diff --git a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImpl.java b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImpl.java index 2298022..46f1095 100644 --- a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImpl.java +++ b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImpl.java @@ -126,10 +126,11 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension { ConfigEntry entry, Patchwork contextExtensionsData ) throws TweedEntryReadException { + TweedReadContext context = new TweedReadWriteContextImpl(this, contextExtensionsData); try { - return getReaderChain(entry).read(reader, entry, new TweedReadWriteContextImpl(this, contextExtensionsData)); + return getReaderChain(entry).read(reader, entry, context); } catch (TweedDataReadException e) { - throw new TweedEntryReadException("Failed to read entry", e); + throw new TweedEntryReadException("Failed to read entry", e, context); } } @@ -140,10 +141,11 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension { ConfigEntry entry, Patchwork contextExtensionsData ) throws TweedEntryWriteException { + TweedWriteContext context = new TweedReadWriteContextImpl(this, contextExtensionsData); try { - getWriterChain(entry).write(writer, value, entry, new TweedReadWriteContextImpl(this, contextExtensionsData)); + getWriterChain(entry).write(writer, value, entry, context); } catch (TweedDataWriteException e) { - throw new TweedEntryWriteException("Failed to write entry", e); + throw new TweedEntryWriteException("Failed to write entry", e, context); } } diff --git a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java index d0fea6c..284ea75 100644 --- a/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java +++ b/tweed5-serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java @@ -38,7 +38,7 @@ public class TweedEntryReaderWriterImpls { private final TweedEntryReader delegate; @Override - public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException { + public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException { if (reader.peekToken().isNull()) { reader.readToken(); return null; @@ -67,21 +67,22 @@ public class TweedEntryReaderWriterImpls { private final BiConsumer writerCall; @Override - public T read(TweedDataReader reader, ConfigEntry entry, TweedReadContext context) throws TweedDataReadException { + public T read(TweedDataReader reader, ConfigEntry entry, TweedReadContext context) { return readerCall.apply(reader.readToken()); } @Override public void write(TweedDataVisitor writer, @Nullable T value, ConfigEntry entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException { - requireNonNullWriteValue(value); + requireNonNullWriteValue(value, context); writerCall.accept(writer, value); } } public static class CollectionReaderWriter> implements TweedEntryReaderWriter> { @Override - public C read(TweedDataReader reader, CollectionConfigEntry entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException { - assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start"); + public C read(TweedDataReader reader, CollectionConfigEntry entry, TweedReadContext context) throws + TweedEntryReadException { + assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start", context); TweedDataToken token = reader.peekToken(); if (token.isListEnd()) { return entry.instantiateCollection(0); @@ -99,7 +100,10 @@ public class TweedEntryReaderWriterImpls { } else if (token.isListValue()) { list.add(elementReader.read(reader, elementEntry, context)); } else { - throw new TweedEntryReadException("Unexpected token " + token + ": expected next list value or list end"); + throw new TweedEntryReadException( + "Unexpected token " + token + ": expected next list value or list end", + context + ); } } @@ -110,7 +114,7 @@ public class TweedEntryReaderWriterImpls { @Override public void write(TweedDataVisitor writer, C value, CollectionConfigEntry entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException { - requireNonNullWriteValue(value); + requireNonNullWriteValue(value, context); if (value.isEmpty()) { writer.visitEmptyList(); @@ -130,8 +134,9 @@ public class TweedEntryReaderWriterImpls { public static class CompoundReaderWriter implements TweedEntryReaderWriter> { @Override - public T read(TweedDataReader reader, CompoundConfigEntry entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException { - assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start"); + public T read(TweedDataReader reader, CompoundConfigEntry entry, TweedReadContext context) throws + TweedEntryReadException { + assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start", context); Map> compoundEntries = entry.subEntries(); T compoundValue = entry.instantiateCompoundValue(); @@ -148,7 +153,10 @@ public class TweedEntryReaderWriterImpls { Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context); entry.set(compoundValue, key, subEntryValue); } 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", + context + ); } } return compoundValue; @@ -156,7 +164,7 @@ public class TweedEntryReaderWriterImpls { @Override public void write(TweedDataVisitor writer, @Nullable T value, CompoundConfigEntry entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException { - requireNonNullWriteValue(value); + requireNonNullWriteValue(value, context); writer.visitMapStart(); @@ -178,7 +186,7 @@ public class TweedEntryReaderWriterImpls { public static class NoopReaderWriter implements TweedEntryReaderWriter<@Nullable Object, ConfigEntry> { @Override - public @Nullable Object read(TweedDataReader reader, ConfigEntry entry, TweedReadContext context) throws TweedDataReadException { + public @Nullable Object read(TweedDataReader reader, ConfigEntry entry, TweedReadContext context) { TweedDataToken token = reader.readToken(); if (!token.isListStart() && !token.isMapStart()) { return null; @@ -216,16 +224,24 @@ public class TweedEntryReaderWriterImpls { } } - @Contract("null -> fail") - private static void requireNonNullWriteValue(@Nullable T value) throws TweedEntryWriteException { + @Contract("null, _ -> fail") + private static void requireNonNullWriteValue( + @Nullable T value, + TweedWriteContext context + ) throws TweedEntryWriteException { if (value == null) { - throw new TweedEntryWriteException("Unable to write null value"); + throw new TweedEntryWriteException("Unable to write null value", context); } } - private static void assertIsToken(TweedDataToken token, Predicate isToken, String description) throws TweedEntryReadException { + private static void assertIsToken( + TweedDataToken token, + Predicate isToken, + String description, + TweedReadContext context + ) throws TweedEntryReadException { if (!isToken.test(token)) { - throw new TweedEntryReadException("Unexpected token " + token + ": " + description); + throw new TweedEntryReadException("Unexpected token " + token + ": " + description, context); } } }