[*] Generalize the entry system by introducing "structured entries"
This commit is contained in:
@@ -99,13 +99,7 @@ public class AttributesExtensionImpl implements AttributesExtension {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean enterCompoundEntry(ConfigEntry<?> entry) {
|
public boolean enterStructuredEntry(ConfigEntry<?> entry) {
|
||||||
enterEntry(entry);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean enterCollectionEntry(ConfigEntry<?> entry) {
|
|
||||||
enterEntry(entry);
|
enterEntry(entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -130,12 +124,7 @@ public class AttributesExtensionImpl implements AttributesExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveCompoundEntry(ConfigEntry<?> entry) {
|
public void leaveStructuredEntry(ConfigEntry<?> entry) {
|
||||||
defaults.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void leaveCollectionEntry(ConfigEntry<?> entry) {
|
|
||||||
defaults.pop();
|
defaults.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,26 +88,14 @@ public class AttributesReadWriteFilterExtensionImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean enterCollectionEntry(ConfigEntry<?> entry) {
|
public boolean enterStructuredEntry(ConfigEntry<?> entry) {
|
||||||
attributesCollectors.push(new HashMap<>());
|
attributesCollectors.push(new HashMap<>());
|
||||||
visitEntry(entry);
|
visitEntry(entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveCollectionEntry(ConfigEntry<?> entry) {
|
public void leaveStructuredEntry(ConfigEntry<?> entry) {
|
||||||
leaveContainerEntry(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean enterCompoundEntry(ConfigEntry<?> entry) {
|
|
||||||
attributesCollectors.push(new HashMap<>());
|
|
||||||
visitEntry(entry);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void leaveCompoundEntry(ConfigEntry<?> entry) {
|
|
||||||
leaveContainerEntry(entry);
|
leaveContainerEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public class CommentLoaderExtensionImpl implements CommentLoaderExtension, Comme
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> commentsByKey = collectingCommentsVisitor.commentsByKey();
|
Map<String, String> commentsByKey = collectingCommentsVisitor.commentsByKey();
|
||||||
PathTracking pathTracking = new PathTracking();
|
PathTracking pathTracking = PathTracking.create();
|
||||||
configContainer.rootEntry().visitInOrder(new PathTrackingConfigEntryVisitor(
|
configContainer.rootEntry().visitInOrder(new PathTrackingConfigEntryVisitor(
|
||||||
entry -> {
|
entry -> {
|
||||||
String key = pathTracking.currentPath();
|
String key = pathTracking.currentPath();
|
||||||
|
|||||||
@@ -1,15 +1,44 @@
|
|||||||
package de.siphalor.tweed5.core.api.entry;
|
package de.siphalor.tweed5.core.api.entry;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
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
|
@Override
|
||||||
default CollectionConfigEntry<E, T> apply(Consumer<ConfigEntry<T>> function) {
|
default CollectionConfigEntry<E, T> apply(Consumer<ConfigEntry<T>> function) {
|
||||||
ConfigEntry.super.apply(function);
|
StructuredConfigEntry.super.apply(function);
|
||||||
return this;
|
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();
|
ConfigEntry<E> elementEntry();
|
||||||
|
|
||||||
T instantiateCollection(int size);
|
T instantiateCollection(int size);
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
package de.siphalor.tweed5.core.api.entry;
|
package de.siphalor.tweed5.core.api.entry;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface CompoundConfigEntry<T> extends ConfigEntry<T> {
|
public interface CompoundConfigEntry<T> extends StructuredConfigEntry<T> {
|
||||||
@Override
|
@Override
|
||||||
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||||
ConfigEntry.super.apply(function);
|
StructuredConfigEntry.super.apply(function);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, ConfigEntry<?>> subEntries();
|
|
||||||
|
|
||||||
<V> void set(T compoundValue, String key, V value);
|
<V> void set(T compoundValue, String key, V value);
|
||||||
<V> V get(T compoundValue, String key);
|
<V> V get(T compoundValue, String key);
|
||||||
|
|
||||||
|
|||||||
@@ -3,26 +3,18 @@ package de.siphalor.tweed5.core.api.entry;
|
|||||||
public interface ConfigEntryValueVisitor {
|
public interface ConfigEntryValueVisitor {
|
||||||
<T> void visitEntry(ConfigEntry<T> entry, T value);
|
<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);
|
visitEntry(entry, value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
|
default boolean enterStructuredSubEntry(String entryKey, String valueKey) {
|
||||||
}
|
|
||||||
|
|
||||||
default <T> boolean enterCompoundEntry(ConfigEntry<T> entry, T value) {
|
|
||||||
visitEntry(entry, value);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean enterCompoundSubEntry(String key) {
|
default void leaveStructuredSubEntry(String entryKey, String valueKey) {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default void leaveCompoundSubEntry(String key) {
|
default <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||||
}
|
|
||||||
|
|
||||||
default <T> void leaveCompoundEntry(ConfigEntry<T> entry, T value) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,26 +3,18 @@ package de.siphalor.tweed5.core.api.entry;
|
|||||||
public interface ConfigEntryVisitor {
|
public interface ConfigEntryVisitor {
|
||||||
void visitEntry(ConfigEntry<?> entry);
|
void visitEntry(ConfigEntry<?> entry);
|
||||||
|
|
||||||
default boolean enterCollectionEntry(ConfigEntry<?> entry) {
|
default boolean enterStructuredEntry(ConfigEntry<?> entry) {
|
||||||
visitEntry(entry);
|
visitEntry(entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void leaveCollectionEntry(ConfigEntry<?> entry) {
|
default boolean enterStructuredSubEntry(String key) {
|
||||||
}
|
|
||||||
|
|
||||||
default boolean enterCompoundEntry(ConfigEntry<?> entry) {
|
|
||||||
visitEntry(entry);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean enterCompoundSubEntry(String key) {
|
default void leaveStructuredSubEntry(String key) {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default void leaveCompoundSubEntry(String key) {
|
default void leaveStructuredEntry(ConfigEntry<?> entry) {
|
||||||
}
|
|
||||||
|
|
||||||
default void leaveCompoundEntry(ConfigEntry<?> entry) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -31,26 +31,6 @@ public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseC
|
|||||||
return collectionConstructor.apply(size);
|
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
|
@Override
|
||||||
public T deepCopy(T value) {
|
public T deepCopy(T value) {
|
||||||
T copy = collectionConstructor.apply(value.size());
|
T copy = collectionConstructor.apply(value.size());
|
||||||
|
|||||||
@@ -51,32 +51,19 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
|
|||||||
return mapConstructor.apply(compoundEntries.size());
|
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
|
@Override
|
||||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||||
if (visitor.enterCompoundEntry(this, value)) {
|
if (visitor.enterStructuredEntry(this, value)) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
compoundEntries.forEach((key, entry) -> {
|
compoundEntries.forEach((key, entry) -> {
|
||||||
if (visitor.enterCompoundSubEntry(key)) {
|
if (visitor.enterStructuredSubEntry(key, key)) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
|
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
|
||||||
visitor.leaveCompoundSubEntry(key);
|
visitor.leaveStructuredSubEntry(key, key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
visitor.leaveCompoundEntry(this, value);
|
visitor.leaveStructuredEntry(this, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +1,15 @@
|
|||||||
package de.siphalor.tweed5.defaultextensions.pather.api;
|
package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import de.siphalor.tweed5.defaultextensions.pather.impl.PathTrackingImpl;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
public interface PathTracking {
|
||||||
import java.util.Deque;
|
static PathTracking create() {
|
||||||
|
return new PathTrackingImpl();
|
||||||
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 void popContext() {
|
void pushPathPart(String pathPart);
|
||||||
if (contextStack.pop() == Context.LIST) {
|
|
||||||
listIndexes.pop();
|
|
||||||
popPathPart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pushMapContext() {
|
void popPathPart();
|
||||||
contextStack.push(Context.MAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pushPathPart(String part) {
|
String currentPath();
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,64 +8,35 @@ import org.jspecify.annotations.Nullable;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisitor {
|
public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisitor {
|
||||||
private final ConfigEntryValueVisitor delegate;
|
private final ConfigEntryValueVisitor delegate;
|
||||||
private final PathTracking pathTracking;
|
private final ValuePathTracking pathTracking;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends @Nullable Object> void visitEntry(ConfigEntry<T> entry, T value) {
|
public <T extends @Nullable Object> void visitEntry(ConfigEntry<T> entry, T value) {
|
||||||
delegate.visitEntry(entry, value);
|
delegate.visitEntry(entry, value);
|
||||||
entryVisited();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> boolean enterCollectionEntry(ConfigEntry<T> entry, T value) {
|
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||||
boolean enter = delegate.enterCollectionEntry(entry, value);
|
return delegate.enterStructuredEntry(entry, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean enterStructuredSubEntry(String entryKey, String valueKey) {
|
||||||
|
boolean enter = delegate.enterStructuredSubEntry(entryKey, valueKey);
|
||||||
if (enter) {
|
if (enter) {
|
||||||
pathTracking.pushListContext();
|
pathTracking.pushPathPart(entryKey, valueKey);
|
||||||
}
|
}
|
||||||
return enter;
|
return enter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
|
public void leaveStructuredSubEntry(String entryKey, String valueKey) {
|
||||||
delegate.leaveCollectionEntry(entry, value);
|
delegate.leaveStructuredSubEntry(entryKey, valueKey);
|
||||||
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);
|
|
||||||
pathTracking.popPathPart();
|
pathTracking.popPathPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void leaveCompoundEntry(ConfigEntry<T> entry, T value) {
|
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||||
delegate.leaveCompoundEntry(entry, value);
|
delegate.leaveStructuredEntry(entry, value);
|
||||||
pathTracking.popContext();
|
|
||||||
entryVisited();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void entryVisited() {
|
|
||||||
if (pathTracking.currentContext() == PathTracking.Context.LIST) {
|
|
||||||
pathTracking.incrementListIndex();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,37 +12,16 @@ public class PathTrackingConfigEntryVisitor implements ConfigEntryVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visitEntry(ConfigEntry<?> entry) {
|
public void visitEntry(ConfigEntry<?> entry) {
|
||||||
delegate.visitEntry(entry);
|
delegate.visitEntry(entry);
|
||||||
entryVisited();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean enterCollectionEntry(ConfigEntry<?> entry) {
|
public boolean enterStructuredEntry(ConfigEntry<?> entry) {
|
||||||
boolean enter = delegate.enterCollectionEntry(entry);
|
return delegate.enterStructuredEntry(entry);
|
||||||
if (enter) {
|
|
||||||
pathTracking.pushListContext();
|
|
||||||
}
|
|
||||||
return enter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveCollectionEntry(ConfigEntry<?> entry) {
|
public boolean enterStructuredSubEntry(String key) {
|
||||||
delegate.leaveCollectionEntry(entry);
|
boolean enter = delegate.enterStructuredSubEntry(key);
|
||||||
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);
|
|
||||||
if (enter) {
|
if (enter) {
|
||||||
pathTracking.pushPathPart(key);
|
pathTracking.pushPathPart(key);
|
||||||
}
|
}
|
||||||
@@ -50,21 +29,13 @@ public class PathTrackingConfigEntryVisitor implements ConfigEntryVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveCompoundSubEntry(String key) {
|
public void leaveStructuredSubEntry(String key) {
|
||||||
delegate.leaveCompoundSubEntry(key);
|
delegate.leaveStructuredSubEntry(key);
|
||||||
pathTracking.popPathPart();
|
pathTracking.popPathPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveCompoundEntry(ConfigEntry<?> entry) {
|
public void leaveStructuredEntry(ConfigEntry<?> entry) {
|
||||||
delegate.leaveCompoundEntry(entry);
|
delegate.leaveStructuredEntry(entry);
|
||||||
pathTracking.popContext();
|
|
||||||
entryVisited();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void entryVisited() {
|
|
||||||
if (pathTracking.currentContext() == PathTracking.Context.LIST) {
|
|
||||||
pathTracking.incrementListIndex();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,14 @@ import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
|||||||
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
|
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PathTrackingDataReader implements TweedDataReader {
|
public class PathTrackingDataReader implements TweedDataReader {
|
||||||
private final TweedDataReader delegate;
|
private final TweedDataReader delegate;
|
||||||
private final PathTracking pathTracking;
|
private final PathTracking pathTracking;
|
||||||
|
private final ArrayDeque<Context> contextStack = new ArrayDeque<>(50);
|
||||||
|
private final ArrayDeque<Integer> listIndexStack = new ArrayDeque<>(50);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TweedDataToken peekToken() throws TweedDataReadException {
|
public TweedDataToken peekToken() throws TweedDataReadException {
|
||||||
@@ -18,21 +22,35 @@ public class PathTrackingDataReader implements TweedDataReader {
|
|||||||
@Override
|
@Override
|
||||||
public TweedDataToken readToken() throws TweedDataReadException {
|
public TweedDataToken readToken() throws TweedDataReadException {
|
||||||
TweedDataToken token = delegate.readToken();
|
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()) {
|
if (token.isListStart()) {
|
||||||
pathTracking.pushListContext();
|
contextStack.push(Context.LIST);
|
||||||
} else if (token.isListValue()) {
|
listIndexStack.push(-1);
|
||||||
pathTracking.incrementListIndex();
|
|
||||||
} else if (token.isListEnd()) {
|
} else if (token.isListEnd()) {
|
||||||
pathTracking.popContext();
|
contextStack.pop();
|
||||||
|
int lastIndex = listIndexStack.pop();
|
||||||
|
if (lastIndex >= 0) {
|
||||||
|
pathTracking.popPathPart();
|
||||||
|
}
|
||||||
} else if (token.isMapStart()) {
|
} else if (token.isMapStart()) {
|
||||||
pathTracking.pushMapContext();
|
contextStack.push(Context.MAP);
|
||||||
pathTracking.pushPathPart("$");
|
pathTracking.pushPathPart("$");
|
||||||
} else if (token.isMapEntryKey()) {
|
} else if (token.isMapEntryKey()) {
|
||||||
pathTracking.popPathPart();
|
pathTracking.popPathPart();
|
||||||
pathTracking.pushPathPart(token.readAsString());
|
pathTracking.pushPathPart(token.readAsString());
|
||||||
} else if (token.isMapEnd()) {
|
} else if (token.isMapEnd()) {
|
||||||
pathTracking.popPathPart();
|
pathTracking.popPathPart();
|
||||||
pathTracking.popContext();
|
contextStack.pop();
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -41,4 +59,8 @@ public class PathTrackingDataReader implements TweedDataReader {
|
|||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
delegate.close();
|
delegate.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum Context {
|
||||||
|
LIST, MAP,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PathTrackingDataVisitor implements TweedDataVisitor {
|
public class PathTrackingDataVisitor implements TweedDataVisitor {
|
||||||
private final TweedDataVisitor delegate;
|
private final TweedDataVisitor delegate;
|
||||||
private final PathTracking pathTracking;
|
private final PathTracking pathTracking;
|
||||||
|
private final ArrayDeque<Context> contextStack = new ArrayDeque<>(50);
|
||||||
|
private final ArrayDeque<Integer> listIndexStack = new ArrayDeque<>(50);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitNull() {
|
public void visitNull() {
|
||||||
@@ -72,42 +76,50 @@ public class PathTrackingDataVisitor implements TweedDataVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void valueVisited() {
|
private void valueVisited() {
|
||||||
if (pathTracking.currentContext() == PathTracking.Context.LIST) {
|
Context context = contextStack.peek();
|
||||||
pathTracking.incrementListIndex();
|
if (context == Context.MAP_ENTRY) {
|
||||||
} else {
|
contextStack.pop();
|
||||||
pathTracking.popPathPart();
|
pathTracking.popPathPart();
|
||||||
|
} else if (context == Context.LIST) {
|
||||||
|
pathTracking.popPathPart();
|
||||||
|
int index = listIndexStack.pop();
|
||||||
|
listIndexStack.push(index + 1);
|
||||||
|
pathTracking.pushPathPart(Integer.toString(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitListStart() {
|
public void visitListStart() {
|
||||||
delegate.visitListStart();
|
delegate.visitListStart();
|
||||||
pathTracking.pushListContext();
|
contextStack.push(Context.LIST);
|
||||||
|
listIndexStack.push(0);
|
||||||
|
pathTracking.pushPathPart("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitListEnd() {
|
public void visitListEnd() {
|
||||||
delegate.visitListEnd();
|
delegate.visitListEnd();
|
||||||
pathTracking.popContext();
|
contextStack.pop();
|
||||||
|
listIndexStack.pop();
|
||||||
|
pathTracking.popPathPart();
|
||||||
valueVisited();
|
valueVisited();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitMapStart() {
|
public void visitMapStart() {
|
||||||
delegate.visitMapStart();
|
delegate.visitMapStart();
|
||||||
pathTracking.pushMapContext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitMapEntryKey(String key) {
|
public void visitMapEntryKey(String key) {
|
||||||
delegate.visitMapEntryKey(key);
|
delegate.visitMapEntryKey(key);
|
||||||
pathTracking.pushPathPart(key);
|
pathTracking.pushPathPart(key);
|
||||||
|
contextStack.push(Context.MAP_ENTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitMapEnd() {
|
public void visitMapEnd() {
|
||||||
delegate.visitMapEnd();
|
delegate.visitMapEnd();
|
||||||
pathTracking.popContext();
|
|
||||||
valueVisited();
|
valueVisited();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,4 +127,8 @@ public class PathTrackingDataVisitor implements TweedDataVisitor {
|
|||||||
public void visitDecoration(TweedDataDecoration decoration) {
|
public void visitDecoration(TweedDataDecoration decoration) {
|
||||||
delegate.visitDecoration(decoration);
|
delegate.visitDecoration(decoration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum Context {
|
||||||
|
LIST, MAP_ENTRY,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.*;
|
||||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
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.TweedDataReader;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
||||||
@@ -71,7 +70,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
|||||||
return castedInner.read(reader, entry, context);
|
return castedInner.read(reader, entry, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
pathTracking = new PathTracking();
|
pathTracking = PathTracking.create();
|
||||||
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||||
try {
|
try {
|
||||||
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context);
|
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context);
|
||||||
@@ -80,7 +79,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
|||||||
if (exceptionPathTracking != null) {
|
if (exceptionPathTracking != null) {
|
||||||
throw new TweedEntryReadException(
|
throw new TweedEntryReadException(
|
||||||
"Exception while reading entry at "
|
"Exception while reading entry at "
|
||||||
+ String.join("/", exceptionPathTracking.currentPath())
|
+ exceptionPathTracking.currentPath()
|
||||||
+ ": " + e.getMessage(),
|
+ ": " + e.getMessage(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
@@ -114,7 +113,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pathTracking = new PathTracking();
|
pathTracking = PathTracking.create();
|
||||||
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||||
try {
|
try {
|
||||||
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
||||||
@@ -123,7 +122,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
|||||||
if (exceptionPathTracking != null) {
|
if (exceptionPathTracking != null) {
|
||||||
throw new TweedEntryWriteException(
|
throw new TweedEntryWriteException(
|
||||||
"Exception while writing entry at "
|
"Exception while writing entry at "
|
||||||
+ String.join("/", exceptionPathTracking.currentPath())
|
+ exceptionPathTracking.currentPath()
|
||||||
+ ": " + e.getMessage(),
|
+ ": " + e.getMessage(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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.PathTracking;
|
||||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingConfigEntryValueVisitor;
|
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingConfigEntryValueVisitor;
|
||||||
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
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.ConfigEntryValidator;
|
||||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
|
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
|
||||||
@@ -177,7 +178,7 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> ValidationIssues validate(ConfigEntry<T> entry, @Nullable T value) {
|
public <T> ValidationIssues validate(ConfigEntry<T> entry, @Nullable T value) {
|
||||||
PathTracking pathTracking = new PathTracking();
|
ValuePathTracking pathTracking = ValuePathTracking.create();
|
||||||
ValidatingConfigEntryVisitor validatingVisitor = new ValidatingConfigEntryVisitor(pathTracking);
|
ValidatingConfigEntryVisitor validatingVisitor = new ValidatingConfigEntryVisitor(pathTracking);
|
||||||
|
|
||||||
entry.visitInOrder(new PathTrackingConfigEntryValueVisitor(validatingVisitor, pathTracking), value);
|
entry.visitInOrder(new PathTrackingConfigEntryValueVisitor(validatingVisitor, pathTracking), value);
|
||||||
@@ -281,22 +282,12 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> boolean enterCollectionEntry(ConfigEntry<T> entry, T value) {
|
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
|
public <T> void leaveStructuredEntry(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) {
|
|
||||||
visitEntry(entry, value);
|
visitEntry(entry, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
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 de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCollectionConfigEntry;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.function.IntFunction;
|
import java.util.function.IntFunction;
|
||||||
@@ -21,7 +18,7 @@ import java.util.function.IntFunction;
|
|||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements WeavableCollectionConfigEntry<E, T> {
|
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements WeavableCollectionConfigEntry<E, T> {
|
||||||
private final IntFunction<T> constructor;
|
private final IntFunction<T> constructor;
|
||||||
private final @Nullable ConfigEntry<E> elementEntry;
|
private final ConfigEntry<E> elementEntry;
|
||||||
|
|
||||||
public CollectionConfigEntryImpl(
|
public CollectionConfigEntryImpl(
|
||||||
ConfigContainer<?> configContainer,
|
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
|
@Override
|
||||||
public @NotNull T deepCopy(T value) {
|
public @NotNull T deepCopy(T value) {
|
||||||
T copy = instantiateCollection(value.size());
|
T copy = instantiateCollection(value.size());
|
||||||
|
|||||||
@@ -86,22 +86,22 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitInOrder(ConfigEntryVisitor visitor) {
|
public void visitInOrder(ConfigEntryVisitor visitor) {
|
||||||
if (visitor.enterCompoundEntry(this)) {
|
if (visitor.enterStructuredEntry(this)) {
|
||||||
subConfigEntries.forEach((key, entry) -> {
|
subConfigEntries.forEach((key, entry) -> {
|
||||||
if (visitor.enterCompoundSubEntry(key)) {
|
if (visitor.enterStructuredSubEntry(key)) {
|
||||||
entry.visitInOrder(visitor);
|
entry.visitInOrder(visitor);
|
||||||
visitor.leaveCompoundSubEntry(key);
|
visitor.leaveStructuredSubEntry(key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
visitor.leaveCompoundEntry(this);
|
visitor.leaveStructuredEntry(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value) {
|
public void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value) {
|
||||||
if (visitor.enterCompoundEntry(this, value)) {
|
if (visitor.enterStructuredEntry(this, value)) {
|
||||||
subEntries.forEach((key, entry) -> {
|
subEntries.forEach((key, entry) -> {
|
||||||
if (visitor.enterCompoundSubEntry(key)) {
|
if (visitor.enterStructuredSubEntry(key, key)) {
|
||||||
try {
|
try {
|
||||||
Object subValue = entry.getter().invoke(value);
|
Object subValue = entry.getter().invoke(value);
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
|
|||||||
Reference in New Issue
Block a user