[*] Generalize the entry system by introducing "structured entries"

This commit is contained in:
2025-08-12 10:04:05 +02:00
parent 6f2e715b2a
commit 83cbe91e35
22 changed files with 307 additions and 288 deletions

View File

@@ -99,13 +99,7 @@ public class AttributesExtensionImpl implements AttributesExtension {
);
@Override
public boolean enterCompoundEntry(ConfigEntry<?> entry) {
enterEntry(entry);
return true;
}
@Override
public boolean enterCollectionEntry(ConfigEntry<?> entry) {
public boolean enterStructuredEntry(ConfigEntry<?> entry) {
enterEntry(entry);
return true;
}
@@ -130,12 +124,7 @@ public class AttributesExtensionImpl implements AttributesExtension {
}
@Override
public void leaveCompoundEntry(ConfigEntry<?> entry) {
defaults.pop();
}
@Override
public void leaveCollectionEntry(ConfigEntry<?> entry) {
public void leaveStructuredEntry(ConfigEntry<?> entry) {
defaults.pop();
}

View File

@@ -88,26 +88,14 @@ public class AttributesReadWriteFilterExtensionImpl
}
@Override
public boolean enterCollectionEntry(ConfigEntry<?> entry) {
public boolean enterStructuredEntry(ConfigEntry<?> entry) {
attributesCollectors.push(new HashMap<>());
visitEntry(entry);
return true;
}
@Override
public void leaveCollectionEntry(ConfigEntry<?> entry) {
leaveContainerEntry(entry);
}
@Override
public boolean enterCompoundEntry(ConfigEntry<?> entry) {
attributesCollectors.push(new HashMap<>());
visitEntry(entry);
return true;
}
@Override
public void leaveCompoundEntry(ConfigEntry<?> entry) {
public void leaveStructuredEntry(ConfigEntry<?> entry) {
leaveContainerEntry(entry);
}

View File

@@ -93,7 +93,7 @@ public class CommentLoaderExtensionImpl implements CommentLoaderExtension, Comme
}
Map<String, String> commentsByKey = collectingCommentsVisitor.commentsByKey();
PathTracking pathTracking = new PathTracking();
PathTracking pathTracking = PathTracking.create();
configContainer.rootEntry().visitInOrder(new PathTrackingConfigEntryVisitor(
entry -> {
String key = pathTracking.currentPath();

View File

@@ -1,15 +1,44 @@
package de.siphalor.tweed5.core.api.entry;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
public interface CollectionConfigEntry<E, T extends Collection<E>> extends ConfigEntry<T> {
public interface CollectionConfigEntry<E, T extends Collection<E>> extends StructuredConfigEntry<T> {
@Override
default CollectionConfigEntry<E, T> apply(Consumer<ConfigEntry<T>> function) {
ConfigEntry.super.apply(function);
StructuredConfigEntry.super.apply(function);
return this;
}
@Override
default void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value) {
if (value == null) {
return;
}
if (visitor.enterStructuredEntry(this, value)) {
int index = 0;
for (E item : value) {
String indexString = Integer.toString(index);
if (visitor.enterStructuredSubEntry("element", indexString)) {
elementEntry().visitInOrder(visitor, item);
visitor.leaveStructuredSubEntry("element", indexString);
}
index++;
}
visitor.leaveStructuredEntry(this, value);
}
}
@Override
default Map<String, ConfigEntry<?>> subEntries() {
return Collections.singletonMap("element", elementEntry());
}
ConfigEntry<E> elementEntry();
T instantiateCollection(int size);

View File

@@ -1,17 +1,14 @@
package de.siphalor.tweed5.core.api.entry;
import java.util.Map;
import java.util.function.Consumer;
public interface CompoundConfigEntry<T> extends ConfigEntry<T> {
public interface CompoundConfigEntry<T> extends StructuredConfigEntry<T> {
@Override
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
ConfigEntry.super.apply(function);
StructuredConfigEntry.super.apply(function);
return this;
}
Map<String, ConfigEntry<?>> subEntries();
<V> void set(T compoundValue, String key, V value);
<V> V get(T compoundValue, String key);

View File

@@ -3,26 +3,18 @@ package de.siphalor.tweed5.core.api.entry;
public interface ConfigEntryValueVisitor {
<T> void visitEntry(ConfigEntry<T> entry, T value);
default <T> boolean enterCollectionEntry(ConfigEntry<T> entry, T value) {
default <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
visitEntry(entry, value);
return true;
}
default <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
}
default <T> boolean enterCompoundEntry(ConfigEntry<T> entry, T value) {
visitEntry(entry, value);
default boolean enterStructuredSubEntry(String entryKey, String valueKey) {
return true;
}
default boolean enterCompoundSubEntry(String key) {
return true;
default void leaveStructuredSubEntry(String entryKey, String valueKey) {
}
default void leaveCompoundSubEntry(String key) {
}
default <T> void leaveCompoundEntry(ConfigEntry<T> entry, T value) {
default <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
}
}

View File

@@ -3,26 +3,18 @@ package de.siphalor.tweed5.core.api.entry;
public interface ConfigEntryVisitor {
void visitEntry(ConfigEntry<?> entry);
default boolean enterCollectionEntry(ConfigEntry<?> entry) {
default boolean enterStructuredEntry(ConfigEntry<?> entry) {
visitEntry(entry);
return true;
}
default void leaveCollectionEntry(ConfigEntry<?> entry) {
}
default boolean enterCompoundEntry(ConfigEntry<?> entry) {
visitEntry(entry);
default boolean enterStructuredSubEntry(String key) {
return true;
}
default boolean enterCompoundSubEntry(String key) {
return true;
default void leaveStructuredSubEntry(String key) {
}
default void leaveCompoundSubEntry(String key) {
}
default void leaveCompoundEntry(ConfigEntry<?> entry) {
default void leaveStructuredEntry(ConfigEntry<?> entry) {
}
}

View File

@@ -0,0 +1,27 @@
package de.siphalor.tweed5.core.api.entry;
import java.util.Map;
import java.util.function.Consumer;
public interface StructuredConfigEntry<T> extends ConfigEntry<T> {
@Override
default StructuredConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
ConfigEntry.super.apply(function);
return this;
}
@Override
default void visitInOrder(ConfigEntryVisitor visitor) {
if (visitor.enterStructuredEntry(this)) {
subEntries().forEach((key, entry) -> {
if (visitor.enterStructuredSubEntry(key)) {
entry.visitInOrder(visitor);
visitor.leaveStructuredSubEntry(key);
}
});
visitor.leaveStructuredEntry(this);
}
}
Map<String, ConfigEntry<?>> subEntries();
}

View File

@@ -31,26 +31,6 @@ public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseC
return collectionConstructor.apply(size);
}
@Override
public void visitInOrder(ConfigEntryVisitor visitor) {
if (visitor.enterCollectionEntry(this)) {
elementEntry.visitInOrder(visitor);
visitor.leaveCollectionEntry(this);
}
}
@Override
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterCollectionEntry(this, value)) {
if (value != null) {
for (E element : value) {
visitor.visitEntry(elementEntry, element);
}
}
visitor.leaveCollectionEntry(this, value);
}
}
@Override
public T deepCopy(T value) {
T copy = collectionConstructor.apply(value.size());

View File

@@ -51,32 +51,19 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
return mapConstructor.apply(compoundEntries.size());
}
@Override
public void visitInOrder(ConfigEntryVisitor visitor) {
if (visitor.enterCompoundEntry(this)) {
compoundEntries.forEach((key, entry) -> {
if (visitor.enterCompoundSubEntry(key)) {
entry.visitInOrder(visitor);
visitor.leaveCompoundSubEntry(key);
}
});
visitor.leaveCompoundEntry(this);
}
}
@Override
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterCompoundEntry(this, value)) {
if (visitor.enterStructuredEntry(this, value)) {
if (value != null) {
compoundEntries.forEach((key, entry) -> {
if (visitor.enterCompoundSubEntry(key)) {
if (visitor.enterStructuredSubEntry(key, key)) {
//noinspection unchecked
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
visitor.leaveCompoundSubEntry(key);
visitor.leaveStructuredSubEntry(key, key);
}
});
}
visitor.leaveCompoundEntry(this, value);
visitor.leaveStructuredEntry(this, value);
}
}

View File

@@ -1,62 +1,15 @@
package de.siphalor.tweed5.defaultextensions.pather.api;
import org.jspecify.annotations.Nullable;
import de.siphalor.tweed5.defaultextensions.pather.impl.PathTrackingImpl;
import java.util.ArrayDeque;
import java.util.Deque;
public class PathTracking {
private final StringBuilder pathBuilder = new StringBuilder(256);
private final Deque<Context> contextStack = new ArrayDeque<>(50);
private final Deque<String> pathParts = new ArrayDeque<>(50);
private final Deque<Integer> listIndexes = new ArrayDeque<>(10);
public @Nullable Context currentContext() {
return contextStack.peek();
public interface PathTracking {
static PathTracking create() {
return new PathTrackingImpl();
}
public void popContext() {
if (contextStack.pop() == Context.LIST) {
listIndexes.pop();
popPathPart();
}
}
void pushPathPart(String pathPart);
public void pushMapContext() {
contextStack.push(Context.MAP);
}
void popPathPart();
public void pushPathPart(String part) {
pathParts.push(part);
pathBuilder.append(".").append(part);
}
public void popPathPart() {
if (!pathParts.isEmpty()) {
String poppedPart = pathParts.pop();
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
}
}
public void pushListContext() {
contextStack.push(Context.LIST);
listIndexes.push(0);
pushPathPart("0");
}
public int incrementListIndex() {
int index = listIndexes.pop() + 1;
listIndexes.push(index);
popPathPart();
pushPathPart(Integer.toString(index));
return index;
}
public String currentPath() {
return pathBuilder.toString();
}
public enum Context {
LIST, MAP,
}
String currentPath();
}

View File

@@ -8,64 +8,35 @@ import org.jspecify.annotations.Nullable;
@RequiredArgsConstructor
public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisitor {
private final ConfigEntryValueVisitor delegate;
private final PathTracking pathTracking;
private final ValuePathTracking pathTracking;
@Override
public <T extends @Nullable Object> void visitEntry(ConfigEntry<T> entry, T value) {
delegate.visitEntry(entry, value);
entryVisited();
}
@Override
public <T> boolean enterCollectionEntry(ConfigEntry<T> entry, T value) {
boolean enter = delegate.enterCollectionEntry(entry, value);
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
return delegate.enterStructuredEntry(entry, value);
}
@Override
public boolean enterStructuredSubEntry(String entryKey, String valueKey) {
boolean enter = delegate.enterStructuredSubEntry(entryKey, valueKey);
if (enter) {
pathTracking.pushListContext();
pathTracking.pushPathPart(entryKey, valueKey);
}
return enter;
}
@Override
public <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
delegate.leaveCollectionEntry(entry, value);
pathTracking.popContext();
entryVisited();
}
@Override
public <T> boolean enterCompoundEntry(ConfigEntry<T> entry, T value) {
boolean enter = delegate.enterCompoundEntry(entry, value);
if (enter) {
pathTracking.pushMapContext();
}
return enter;
}
@Override
public boolean enterCompoundSubEntry(String key) {
boolean enter = delegate.enterCompoundSubEntry(key);
if (enter) {
pathTracking.pushPathPart(key);
}
return enter;
}
@Override
public void leaveCompoundSubEntry(String key) {
delegate.leaveCompoundSubEntry(key);
public void leaveStructuredSubEntry(String entryKey, String valueKey) {
delegate.leaveStructuredSubEntry(entryKey, valueKey);
pathTracking.popPathPart();
}
@Override
public <T> void leaveCompoundEntry(ConfigEntry<T> entry, T value) {
delegate.leaveCompoundEntry(entry, value);
pathTracking.popContext();
entryVisited();
}
private void entryVisited() {
if (pathTracking.currentContext() == PathTracking.Context.LIST) {
pathTracking.incrementListIndex();
}
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
delegate.leaveStructuredEntry(entry, value);
}
}

View File

@@ -12,37 +12,16 @@ public class PathTrackingConfigEntryVisitor implements ConfigEntryVisitor {
@Override
public void visitEntry(ConfigEntry<?> entry) {
delegate.visitEntry(entry);
entryVisited();
}
@Override
public boolean enterCollectionEntry(ConfigEntry<?> entry) {
boolean enter = delegate.enterCollectionEntry(entry);
if (enter) {
pathTracking.pushListContext();
}
return enter;
public boolean enterStructuredEntry(ConfigEntry<?> entry) {
return delegate.enterStructuredEntry(entry);
}
@Override
public void leaveCollectionEntry(ConfigEntry<?> entry) {
delegate.leaveCollectionEntry(entry);
pathTracking.popContext();
entryVisited();
}
@Override
public boolean enterCompoundEntry(ConfigEntry<?> entry) {
boolean enter = delegate.enterCompoundEntry(entry);
if (enter) {
pathTracking.pushMapContext();
}
return enter;
}
@Override
public boolean enterCompoundSubEntry(String key) {
boolean enter = delegate.enterCompoundSubEntry(key);
public boolean enterStructuredSubEntry(String key) {
boolean enter = delegate.enterStructuredSubEntry(key);
if (enter) {
pathTracking.pushPathPart(key);
}
@@ -50,21 +29,13 @@ public class PathTrackingConfigEntryVisitor implements ConfigEntryVisitor {
}
@Override
public void leaveCompoundSubEntry(String key) {
delegate.leaveCompoundSubEntry(key);
public void leaveStructuredSubEntry(String key) {
delegate.leaveStructuredSubEntry(key);
pathTracking.popPathPart();
}
@Override
public void leaveCompoundEntry(ConfigEntry<?> entry) {
delegate.leaveCompoundEntry(entry);
pathTracking.popContext();
entryVisited();
}
private void entryVisited() {
if (pathTracking.currentContext() == PathTracking.Context.LIST) {
pathTracking.incrementListIndex();
}
public void leaveStructuredEntry(ConfigEntry<?> entry) {
delegate.leaveStructuredEntry(entry);
}
}

View File

@@ -5,10 +5,14 @@ import de.siphalor.tweed5.dataapi.api.TweedDataReader;
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
import lombok.RequiredArgsConstructor;
import java.util.ArrayDeque;
@RequiredArgsConstructor
public class PathTrackingDataReader implements TweedDataReader {
private final TweedDataReader delegate;
private final PathTracking pathTracking;
private final ArrayDeque<Context> contextStack = new ArrayDeque<>(50);
private final ArrayDeque<Integer> listIndexStack = new ArrayDeque<>(50);
@Override
public TweedDataToken peekToken() throws TweedDataReadException {
@@ -18,21 +22,35 @@ public class PathTrackingDataReader implements TweedDataReader {
@Override
public TweedDataToken readToken() throws TweedDataReadException {
TweedDataToken token = delegate.readToken();
if (token.isListValue()) {
if (contextStack.peek() == Context.LIST) {
int index = listIndexStack.pop() + 1;
if (index != 0) {
pathTracking.popPathPart();
}
pathTracking.pushPathPart(Integer.toString(index));
listIndexStack.push(index);
}
}
if (token.isListStart()) {
pathTracking.pushListContext();
} else if (token.isListValue()) {
pathTracking.incrementListIndex();
contextStack.push(Context.LIST);
listIndexStack.push(-1);
} else if (token.isListEnd()) {
pathTracking.popContext();
contextStack.pop();
int lastIndex = listIndexStack.pop();
if (lastIndex >= 0) {
pathTracking.popPathPart();
}
} else if (token.isMapStart()) {
pathTracking.pushMapContext();
contextStack.push(Context.MAP);
pathTracking.pushPathPart("$");
} else if (token.isMapEntryKey()) {
pathTracking.popPathPart();
pathTracking.pushPathPart(token.readAsString());
} else if (token.isMapEnd()) {
pathTracking.popPathPart();
pathTracking.popContext();
contextStack.pop();
}
return token;
}
@@ -41,4 +59,8 @@ public class PathTrackingDataReader implements TweedDataReader {
public void close() throws Exception {
delegate.close();
}
private enum Context {
LIST, MAP,
}
}

View File

@@ -6,10 +6,14 @@ import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
import lombok.RequiredArgsConstructor;
import org.jspecify.annotations.Nullable;
import java.util.ArrayDeque;
@RequiredArgsConstructor
public class PathTrackingDataVisitor implements TweedDataVisitor {
private final TweedDataVisitor delegate;
private final PathTracking pathTracking;
private final ArrayDeque<Context> contextStack = new ArrayDeque<>(50);
private final ArrayDeque<Integer> listIndexStack = new ArrayDeque<>(50);
@Override
public void visitNull() {
@@ -72,42 +76,50 @@ public class PathTrackingDataVisitor implements TweedDataVisitor {
}
private void valueVisited() {
if (pathTracking.currentContext() == PathTracking.Context.LIST) {
pathTracking.incrementListIndex();
} else {
Context context = contextStack.peek();
if (context == Context.MAP_ENTRY) {
contextStack.pop();
pathTracking.popPathPart();
} else if (context == Context.LIST) {
pathTracking.popPathPart();
int index = listIndexStack.pop();
listIndexStack.push(index + 1);
pathTracking.pushPathPart(Integer.toString(index));
}
}
@Override
public void visitListStart() {
delegate.visitListStart();
pathTracking.pushListContext();
contextStack.push(Context.LIST);
listIndexStack.push(0);
pathTracking.pushPathPart("0");
}
@Override
public void visitListEnd() {
delegate.visitListEnd();
pathTracking.popContext();
contextStack.pop();
listIndexStack.pop();
pathTracking.popPathPart();
valueVisited();
}
@Override
public void visitMapStart() {
delegate.visitMapStart();
pathTracking.pushMapContext();
}
@Override
public void visitMapEntryKey(String key) {
delegate.visitMapEntryKey(key);
pathTracking.pushPathPart(key);
contextStack.push(Context.MAP_ENTRY);
}
@Override
public void visitMapEnd() {
delegate.visitMapEnd();
pathTracking.popContext();
valueVisited();
}
@@ -115,4 +127,8 @@ public class PathTrackingDataVisitor implements TweedDataVisitor {
public void visitDecoration(TweedDataDecoration decoration) {
delegate.visitDecoration(decoration);
}
private enum Context {
LIST, MAP_ENTRY,
}
}

View File

@@ -0,0 +1,34 @@
package de.siphalor.tweed5.defaultextensions.pather.api;
import lombok.NoArgsConstructor;
@NoArgsConstructor(staticName = "create")
public final class ValuePathTracking implements PathTracking {
private final PathTracking entryPathTracking = PathTracking.create();
private final PathTracking valuePathTracking = PathTracking.create();
@Override
public void pushPathPart(String pathPart) {
this.pushPathPart(pathPart, pathPart);
}
public void pushPathPart(String entryPathPart, String valuePathPart) {
entryPathTracking.pushPathPart(entryPathPart);
valuePathTracking.pushPathPart(valuePathPart);
}
@Override
public void popPathPart() {
valuePathTracking.popPathPart();
entryPathTracking.popPathPart();
}
@Override
public String currentPath() {
return valuePathTracking.currentPath();
}
public String currentEntryPath() {
return entryPathTracking.currentPath();
}
}

View File

@@ -0,0 +1,30 @@
package de.siphalor.tweed5.defaultextensions.pather.impl;
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
import java.util.ArrayDeque;
import java.util.Deque;
public class PathTrackingImpl implements PathTracking {
private final StringBuilder pathBuilder = new StringBuilder(256);
private final Deque<String> pathParts = new ArrayDeque<>(50);
@Override
public void pushPathPart(String entryPathPart) {
pathParts.push(entryPathPart);
pathBuilder.append(".").append(entryPathPart);
}
@Override
public void popPathPart() {
if (!pathParts.isEmpty()) {
String poppedPart = pathParts.pop();
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
}
}
@Override
public String currentPath() {
return pathBuilder.toString();
}
}

View File

@@ -6,7 +6,6 @@ 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;
@@ -71,7 +70,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
return castedInner.read(reader, entry, context);
}
pathTracking = new PathTracking();
pathTracking = PathTracking.create();
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
try {
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context);
@@ -80,7 +79,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
if (exceptionPathTracking != null) {
throw new TweedEntryReadException(
"Exception while reading entry at "
+ String.join("/", exceptionPathTracking.currentPath())
+ exceptionPathTracking.currentPath()
+ ": " + e.getMessage(),
e
);
@@ -114,7 +113,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
return;
}
pathTracking = new PathTracking();
pathTracking = PathTracking.create();
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
try {
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
@@ -123,7 +122,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
if (exceptionPathTracking != null) {
throw new TweedEntryWriteException(
"Exception while writing entry at "
+ String.join("/", exceptionPathTracking.currentPath())
+ exceptionPathTracking.currentPath()
+ ": " + e.getMessage(),
e
);

View File

@@ -20,6 +20,7 @@ import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingConfigEntryValueVisitor;
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
import de.siphalor.tweed5.defaultextensions.pather.api.ValuePathTracking;
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
@@ -177,7 +178,7 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
@Override
public <T> ValidationIssues validate(ConfigEntry<T> entry, @Nullable T value) {
PathTracking pathTracking = new PathTracking();
ValuePathTracking pathTracking = ValuePathTracking.create();
ValidatingConfigEntryVisitor validatingVisitor = new ValidatingConfigEntryVisitor(pathTracking);
entry.visitInOrder(new PathTrackingConfigEntryValueVisitor(validatingVisitor, pathTracking), value);
@@ -281,22 +282,12 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
}
@Override
public <T> boolean enterCollectionEntry(ConfigEntry<T> entry, T value) {
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
return true;
}
@Override
public <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
visitEntry(entry, value);
}
@Override
public <T> boolean enterCompoundEntry(ConfigEntry<T> entry, T value) {
return true;
}
@Override
public <T> void leaveCompoundEntry(ConfigEntry<T> entry, T value) {
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
visitEntry(entry, value);
}
}

View File

@@ -0,0 +1,72 @@
package de.siphalor.tweed5.defaultextensions.pather.api;
import de.siphalor.tweed5.dataapi.api.TweedDataReadException;
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
import de.siphalor.tweed5.dataapi.api.TweedDataTokens;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class PathTrackingDataReaderTest {
@SneakyThrows
@Test
void test() {
PathTracking pathTracking = PathTracking.create();
TweedDataReader mockedDelegate = mock(TweedDataReader.class);
when(mockedDelegate.readToken()).thenReturn(
TweedDataTokens.getMapStart(),
TweedDataTokens.asMapEntryKey(new StringToken("key")),
TweedDataTokens.asMapEntryValue(new StringToken("value")),
TweedDataTokens.asMapEntryKey(new StringToken("list")),
TweedDataTokens.asMapEntryValue(TweedDataTokens.getListStart()),
TweedDataTokens.asListValue(new StringToken("first")),
TweedDataTokens.asListValue(TweedDataTokens.getMapStart()),
TweedDataTokens.asListValue(TweedDataTokens.getMapEnd()),
TweedDataTokens.getListEnd(),
TweedDataTokens.getMapEnd()
);
var reader = new PathTrackingDataReader(mockedDelegate, pathTracking);
assertThat(reader.readToken()).isEqualTo(TweedDataTokens.getMapStart());
assertThat(pathTracking.currentPath()).isEqualTo(".$");
assertThat(reader.readToken().readAsString()).isEqualTo("key");
assertThat(pathTracking.currentPath()).isEqualTo(".key");
assertThat(reader.readToken().readAsString()).isEqualTo("value");
assertThat(reader.readToken().readAsString()).isEqualTo("list");
assertThat(pathTracking.currentPath()).isEqualTo(".list");
assertThat(reader.readToken().isListStart()).isTrue();
assertThat(pathTracking.currentPath()).isEqualTo(".list");
assertThat(reader.readToken().readAsString()).isEqualTo("first");
assertThat(reader.readToken().isMapStart()).isTrue();
assertThat(pathTracking.currentPath()).startsWith(".list.1");
assertThat(reader.readToken().isMapEnd()).isTrue();
assertThat(reader.readToken().isListEnd()).isTrue();
assertThat(pathTracking.currentPath()).isEqualTo(".list");
assertThat(reader.readToken()).isEqualTo(TweedDataTokens.getMapEnd());
assertThat(pathTracking.currentPath()).isEqualTo("");
}
@RequiredArgsConstructor
@EqualsAndHashCode
static class StringToken implements TweedDataToken {
private final String value;
@Override
public boolean canReadAsString() {
return true;
}
@Override
public String readAsString() {
return value;
}
}
}

View File

@@ -4,14 +4,11 @@ import de.siphalor.tweed5.construct.api.ConstructParameter;
import de.siphalor.tweed5.core.api.container.ConfigContainer;
import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCollectionConfigEntry;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.function.IntFunction;
@@ -21,7 +18,7 @@ import java.util.function.IntFunction;
@ToString(callSuper = true)
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements WeavableCollectionConfigEntry<E, T> {
private final IntFunction<T> constructor;
private final @Nullable ConfigEntry<E> elementEntry;
private final ConfigEntry<E> elementEntry;
public CollectionConfigEntryImpl(
ConfigContainer<?> configContainer,
@@ -43,24 +40,6 @@ public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseC
}
}
@Override
public void visitInOrder(ConfigEntryVisitor visitor) {
if (visitor.enterCollectionEntry(this)) {
elementEntry.visitInOrder(visitor);
visitor.leaveCollectionEntry(this);
}
}
@Override
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterCollectionEntry(this, value)) {
for (E element : value) {
elementEntry.visitInOrder(visitor, element);
}
visitor.leaveCollectionEntry(this, value);
}
}
@Override
public @NotNull T deepCopy(T value) {
T copy = instantiateCollection(value.size());

View File

@@ -86,22 +86,22 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
@Override
public void visitInOrder(ConfigEntryVisitor visitor) {
if (visitor.enterCompoundEntry(this)) {
if (visitor.enterStructuredEntry(this)) {
subConfigEntries.forEach((key, entry) -> {
if (visitor.enterCompoundSubEntry(key)) {
if (visitor.enterStructuredSubEntry(key)) {
entry.visitInOrder(visitor);
visitor.leaveCompoundSubEntry(key);
visitor.leaveStructuredSubEntry(key);
}
});
visitor.leaveCompoundEntry(this);
visitor.leaveStructuredEntry(this);
}
}
@Override
public void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value) {
if (visitor.enterCompoundEntry(this, value)) {
if (visitor.enterStructuredEntry(this, value)) {
subEntries.forEach((key, entry) -> {
if (visitor.enterCompoundSubEntry(key)) {
if (visitor.enterStructuredSubEntry(key, key)) {
try {
Object subValue = entry.getter().invoke(value);
//noinspection unchecked