refactor and improvement to ValueWalker.walk
This commit is contained in:
@@ -18,6 +18,8 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.woggioni.worth.utils.WorthUtils.tail;
|
||||
|
||||
public abstract class ValueDumper implements Dumper {
|
||||
|
||||
protected final Value.Configuration cfg;
|
||||
@@ -30,9 +32,10 @@ public abstract class ValueDumper implements Dumper {
|
||||
|
||||
protected Map<ValueIdentity, Integer> getIdMap(Value root) {
|
||||
Map<ValueIdentity, Integer> occurrencies = new HashMap<>();
|
||||
ValueVisitor visitor = new ValueVisitor(){
|
||||
ValueVisitor visitor = new ValueVisitor<Void>() {
|
||||
@Override
|
||||
public boolean filter(Value value, TraversalContext ctx) {
|
||||
public boolean visitPre(TraversalContext<Void> ctx) {
|
||||
Value value = tail(ctx.getStack()).getValue();
|
||||
if (value.type() == Value.Type.ARRAY || value.type() == Value.Type.OBJECT) {
|
||||
ValueIdentity identity = new ValueIdentity(value);
|
||||
Integer i = occurrencies.getOrDefault(identity, 0);
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
abstract class AbstractStackElement<T> implements StackElement<T> {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private T context;
|
||||
|
||||
private final Value value;
|
||||
|
||||
boolean traverseChildren;
|
||||
|
||||
@Override
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
@@ -1,15 +1,15 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.woggioni.worth.exception.NotImplementedException;
|
||||
import net.woggioni.worth.value.ArrayValue;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ArrayStackElement extends StackElement {
|
||||
import static net.woggioni.worth.utils.WorthUtils.newThrowable;
|
||||
|
||||
@Getter
|
||||
private final ArrayValue value;
|
||||
class ArrayStackElement<T> extends AbstractStackElement<T> {
|
||||
|
||||
private final Iterator<Value> it;
|
||||
|
||||
@@ -19,24 +19,28 @@ public class ArrayStackElement extends StackElement {
|
||||
private Value currentValue = null;
|
||||
|
||||
ArrayStackElement(ArrayValue av) {
|
||||
this.value = av;
|
||||
super(av);
|
||||
it = av.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
Value current() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
Value next() {
|
||||
currentValue = it.next();
|
||||
currentIndex++;
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentKey() {
|
||||
throw newThrowable(NotImplementedException.class,
|
||||
"currentKey not supported for value of type '%s'", getValue().type());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentIndex() {
|
||||
return currentIndex;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,24 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.exception.NotImplementedException;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import static net.woggioni.worth.utils.WorthUtils.newThrowable;
|
||||
|
||||
class LeafStackElement<T> extends AbstractStackElement<T> {
|
||||
public LeafStackElement(Value value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentKey() {
|
||||
throw newThrowable(NotImplementedException.class,
|
||||
"currentKey not supported for value of type '%s'", getValue().type());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentIndex() {
|
||||
throw newThrowable(NotImplementedException.class,
|
||||
"currentIndex not supported for value of type '%s'", getValue().type());
|
||||
}
|
||||
}
|
@@ -7,13 +7,10 @@ import net.woggioni.worth.xface.Value;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class ObjectStackElement extends StackElement {
|
||||
class ObjectStackElement<T> extends AbstractStackElement<T> {
|
||||
|
||||
private final Iterator<Map.Entry<String, Value>> it;
|
||||
|
||||
@Getter
|
||||
private final ObjectValue value;
|
||||
|
||||
@Getter
|
||||
private int currentIndex;
|
||||
|
||||
@@ -23,18 +20,12 @@ public class ObjectStackElement extends StackElement {
|
||||
private Value currentValue;
|
||||
|
||||
public ObjectStackElement(ObjectValue ov) {
|
||||
value = ov;
|
||||
super(ov);
|
||||
it = ov.iterator();
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
Value current() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
Value next() {
|
||||
public Value next() {
|
||||
Map.Entry<String, Value> result = it.next();
|
||||
currentKey = result.getKey();
|
||||
currentIndex++;
|
||||
@@ -42,8 +33,17 @@ public class ObjectStackElement extends StackElement {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNext() {
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentKey() {
|
||||
return currentKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentIndex() {
|
||||
return currentIndex;
|
||||
}
|
||||
}
|
||||
|
@@ -2,8 +2,10 @@ package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
abstract class StackElement {
|
||||
abstract Value current();
|
||||
abstract Value next();
|
||||
abstract boolean hasNext();
|
||||
public interface StackElement<T> {
|
||||
T getContext();
|
||||
void setContext(T ctx);
|
||||
Value getValue();
|
||||
String getCurrentKey();
|
||||
int getCurrentIndex();
|
||||
}
|
||||
|
@@ -1,11 +1,8 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TraversalContext {
|
||||
Value getRoot();
|
||||
List<StackElement> getStack();
|
||||
public interface TraversalContext<T> {
|
||||
List<StackElement<T>> getStack();
|
||||
String getPath();
|
||||
}
|
||||
|
@@ -1,16 +1,6 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.value.*;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
public interface ValueVisitor {
|
||||
|
||||
default void visit(ObjectValue value, TraversalContext ctx) {}
|
||||
default void visit(ArrayValue value, TraversalContext ctx) {}
|
||||
default void visit(BooleanValue value, TraversalContext ctx) {}
|
||||
default void visit(StringValue value, TraversalContext ctx) {}
|
||||
default void visit(IntegerValue value, TraversalContext ctx) {}
|
||||
default void visit(FloatValue value, TraversalContext ctx) {}
|
||||
default void visit(NullValue value, TraversalContext ctx) {}
|
||||
default boolean filter(Value value, TraversalContext ctx) { return true; }
|
||||
public interface ValueVisitor<T> {
|
||||
default boolean visitPre(TraversalContext<T> ctx) { return true; }
|
||||
default void visitPost(TraversalContext<T> ctx) {}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.value.*;
|
||||
import net.woggioni.worth.value.ArrayValue;
|
||||
import net.woggioni.worth.value.ObjectValue;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -10,6 +11,40 @@ import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static net.woggioni.worth.utils.WorthUtils.dynamicCast;
|
||||
import static net.woggioni.worth.utils.WorthUtils.pop;
|
||||
import static net.woggioni.worth.utils.WorthUtils.tail;
|
||||
|
||||
class TraversalContextImpl<T> implements TraversalContext<T> {
|
||||
private final List<StackElement<T>> immutableStack;
|
||||
|
||||
public TraversalContextImpl(List<? extends StackElement<T>> stack) {
|
||||
immutableStack = Collections.unmodifiableList(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StackElement<T>> getStack() {
|
||||
return immutableStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (StackElement se : immutableStack) {
|
||||
ArrayStackElement ase;
|
||||
ObjectStackElement ose;
|
||||
if ((ase = dynamicCast(se, ArrayStackElement.class)) != null) {
|
||||
sb.append("[");
|
||||
sb.append(ase.getCurrentIndex());
|
||||
sb.append("]");
|
||||
} else if ((ose = dynamicCast(se, ObjectStackElement.class)) != null) {
|
||||
sb.append("[\"");
|
||||
sb.append(ose.getCurrentKey());
|
||||
sb.append("\"]");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public class ValueWalker {
|
||||
|
||||
@@ -65,91 +100,56 @@ public class ValueWalker {
|
||||
return parent.type() == Value.Type.NULL;
|
||||
}
|
||||
|
||||
public static void walk(Value root, ValueVisitor visitor) {
|
||||
List<StackElement> stack = new ArrayList<>();
|
||||
List<StackElement> immutableStack = Collections.unmodifiableList(stack);
|
||||
|
||||
TraversalContext ctx = new TraversalContext() {
|
||||
|
||||
@Override
|
||||
public Value getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StackElement> getStack() {
|
||||
return immutableStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(StackElement se : stack) {
|
||||
ArrayStackElement ase;
|
||||
ObjectStackElement ose;
|
||||
if((ase = dynamicCast(se, ArrayStackElement.class)) != null) {
|
||||
sb.append("[");
|
||||
sb.append(ase.getCurrentIndex());
|
||||
sb.append("]");
|
||||
} else if((ose = dynamicCast(se, ObjectStackElement.class)) != null) {
|
||||
sb.append("[\"");
|
||||
sb.append(ose.getCurrentKey());
|
||||
sb.append("\"]");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
};
|
||||
|
||||
ObjectValue ov;
|
||||
ArrayValue av = new ArrayValue();
|
||||
av.add(root);
|
||||
ArrayStackElement ase = new ArrayStackElement(av);
|
||||
stack.add(ase);
|
||||
while(true) {
|
||||
Value currentValue = stack.get(stack.size() - 1).next();
|
||||
if(visitor.filter(currentValue, ctx)) {
|
||||
if ((av = dynamicCast(currentValue, ArrayValue.class)) != null) {
|
||||
ase = new ArrayStackElement(av);
|
||||
stack.add(ase);
|
||||
} else if ((ov = dynamicCast(currentValue, ObjectValue.class)) != null) {
|
||||
ObjectStackElement ose = new ObjectStackElement(ov);
|
||||
stack.add(ose);
|
||||
} else {
|
||||
IntegerValue iv;
|
||||
BooleanValue bv;
|
||||
NullValue nv;
|
||||
FloatValue fv;
|
||||
StringValue sv;
|
||||
if ((iv = dynamicCast(currentValue, IntegerValue.class)) != null) {
|
||||
visitor.visit(iv, ctx);
|
||||
} else if ((fv = dynamicCast(currentValue, FloatValue.class)) != null) {
|
||||
visitor.visit(fv, ctx);
|
||||
} else if ((bv = dynamicCast(currentValue, BooleanValue.class)) != null) {
|
||||
visitor.visit(bv, ctx);
|
||||
} else if ((sv = dynamicCast(currentValue, StringValue.class)) != null) {
|
||||
visitor.visit(sv, ctx);
|
||||
} else if ((nv = dynamicCast(currentValue, NullValue.class)) != null) {
|
||||
visitor.visit(nv, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
while(true) {
|
||||
if(stack.size() == 1) return;
|
||||
int lastIndex = stack.size() - 1;
|
||||
StackElement se = stack.get(lastIndex);
|
||||
if(!se.hasNext()) {
|
||||
ObjectStackElement ose;
|
||||
if((ase = dynamicCast(se, ArrayStackElement.class)) != null) {
|
||||
visitor.visit(ase.getValue(), ctx);
|
||||
} else if((ose = dynamicCast(se, ObjectStackElement.class)) != null) {
|
||||
visitor.visit(ose.getValue(), ctx);
|
||||
}
|
||||
stack.remove(lastIndex);
|
||||
} else {
|
||||
private static <T> AbstractStackElement<T> stackElementFromValue(Value value) {
|
||||
AbstractStackElement<T> result;
|
||||
switch (value.type()) {
|
||||
case ARRAY:
|
||||
result = new ArrayStackElement<>((ArrayValue) value);
|
||||
break;
|
||||
case OBJECT:
|
||||
result = new ObjectStackElement<>((ObjectValue) value);
|
||||
break;
|
||||
default:
|
||||
result = new LeafStackElement<>(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <T> AbstractStackElement<T> nextChildStackElement(StackElement<T> parent) {
|
||||
AbstractStackElement<T> result = null;
|
||||
if (parent instanceof ArrayStackElement) {
|
||||
ArrayStackElement ase = (ArrayStackElement) parent;
|
||||
if (ase.hasNext()) {
|
||||
result = stackElementFromValue(ase.next());
|
||||
}
|
||||
} else if (parent instanceof ObjectStackElement) {
|
||||
ObjectStackElement ose = (ObjectStackElement) parent;
|
||||
if (ose.hasNext()) {
|
||||
result = stackElementFromValue(ose.next());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> void walk(Value root, ValueVisitor<T> visitor) {
|
||||
List<AbstractStackElement<T>> stack = new ArrayList<>();
|
||||
TraversalContext<T> ctx = new TraversalContextImpl<>(stack);
|
||||
AbstractStackElement<T> stackElement = stackElementFromValue(root);
|
||||
stack.add(stackElement);
|
||||
stackElement.traverseChildren = visitor.visitPre(ctx);
|
||||
while (!stack.isEmpty()) {
|
||||
AbstractStackElement<T> last = tail(stack);
|
||||
if(last.traverseChildren) {
|
||||
AbstractStackElement<T> childStackElement = nextChildStackElement(last);
|
||||
if(childStackElement != null) {
|
||||
stack.add(childStackElement);
|
||||
childStackElement.traverseChildren = visitor.visitPre(ctx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
visitor.visitPost(ctx);
|
||||
pop(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
249
src/main/java/net/woggioni/worth/utils/ListView.java
Normal file
249
src/main/java/net/woggioni/worth/utils/ListView.java
Normal file
@@ -0,0 +1,249 @@
|
||||
package net.woggioni.worth.utils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ListView<T> implements List<T> {
|
||||
private final List<T> delegate;
|
||||
private final int start;
|
||||
private final int end;
|
||||
|
||||
public ListView(List<T> delegate, int start) {
|
||||
this(delegate, start, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return end < 0 ? delegate.size() : min(end, delegate.size()) - start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
Iterator<T> it = iterator();
|
||||
while (it.hasNext()) {
|
||||
if(Objects.equals(o, it.next())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
int index = start;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return end < 0 ? index < size() : index < min(end, size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return get(index++);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
int size = size();
|
||||
Object[] result = new Object[size];
|
||||
for(int i = 0; i < size; i++) {
|
||||
result[i] = get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T1> T1[] toArray(T1[] t1s) {
|
||||
int size = size();
|
||||
T1[] result = Arrays.copyOf(t1s, size);
|
||||
for(int i = 0; i < size; i++) {
|
||||
result[i] = (T1) get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int i, Collection<? extends T> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int i) {
|
||||
int index = start + i;
|
||||
if(end >= 0 && index < end) {
|
||||
throw new IndexOutOfBoundsException(i);
|
||||
}
|
||||
return delegate.get(start + i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int i, T t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int i, T t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
int size = size();
|
||||
for(int i = 0; i < size; i++) {
|
||||
if(Objects.equals(o, get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
int size = size();
|
||||
for(int i = size - 1; i >= 0; i--) {
|
||||
if(Objects.equals(o, get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator() {
|
||||
return new ListViewIterator<>(this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int i) {
|
||||
if(i < 0 || i > size()) {
|
||||
throw new IndexOutOfBoundsException(0);
|
||||
} else {
|
||||
return new ListViewIterator<>(this, start);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> subList(int i, int i1) {
|
||||
if(i < 0) {
|
||||
throw new IndexOutOfBoundsException(0);
|
||||
} else if(i1 > size()) {
|
||||
throw new IndexOutOfBoundsException(i1);
|
||||
} else {
|
||||
return new ListView<>(delegate, start + i, start + i1);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ListViewIterator<T> implements ListIterator<T> {
|
||||
private final ListView<T> listView;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
public ListViewIterator(ListView<T> listView, int start) {
|
||||
this.listView = listView;
|
||||
size = listView.size();
|
||||
i = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return i < size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return listView.get(i++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return i > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T previous() {
|
||||
return listView.get(--i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,7 +7,9 @@ import java.io.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
@@ -141,4 +143,16 @@ public class WorthUtils {
|
||||
System.setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T tail(List<T> l) {
|
||||
return tail(l, -1);
|
||||
}
|
||||
|
||||
public static <T> T tail(List<T> l, int offset) {
|
||||
return l.get(l.size() + offset);
|
||||
}
|
||||
|
||||
public static <T> T pop(List<T> l) {
|
||||
return l.remove(l.size() - 1);
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package net.woggioni.worth.traversal;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.worth.serialization.json.JSONParser;
|
||||
import net.woggioni.worth.serialization.json.JSONTest;
|
||||
import net.woggioni.worth.utils.Tuple2;
|
||||
import net.woggioni.worth.utils.WorthUtils;
|
||||
import net.woggioni.worth.value.*;
|
||||
import net.woggioni.worth.xface.Parser;
|
||||
@@ -33,65 +34,46 @@ public class ValueWalkerTest {
|
||||
Assert.assertEquals("Amazon", text.get());
|
||||
}
|
||||
|
||||
private static class TestVisitor implements ValueVisitor {
|
||||
private static class TestVisitor implements ValueVisitor<Void> {
|
||||
|
||||
public List<Value> values = new ArrayList<>();
|
||||
public List<Value> preValues = new ArrayList<>();
|
||||
public List<Value> postValues = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void visit(ObjectValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
public boolean visitPre(TraversalContext<Void> ctx) {
|
||||
Value value = WorthUtils.tail(ctx.getStack()).getValue();
|
||||
preValues.add(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ArrayValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BooleanValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(StringValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IntegerValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(FloatValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NullValue value, TraversalContext ctx) {
|
||||
values.add(value);
|
||||
public void visitPost(TraversalContext<Void> ctx) {
|
||||
Value value = WorthUtils.tail(ctx.getStack()).getValue();
|
||||
postValues.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void walk(List<Value> result, Value value) {
|
||||
private void walk(List<Value> preResult, List<Value> postResult, Value value) {
|
||||
ObjectValue ov;
|
||||
ArrayValue av;
|
||||
preResult.add(value);
|
||||
if((av = WorthUtils.dynamicCast(value, ArrayValue.class)) != null) {
|
||||
for(Value v : av) {
|
||||
walk(result, v);
|
||||
walk(preResult, postResult, v);
|
||||
}
|
||||
} else if((ov = WorthUtils.dynamicCast(value, ObjectValue.class)) != null) {
|
||||
for(Map.Entry<String, Value> entry : ov) {
|
||||
walk(result, entry.getValue());
|
||||
walk(preResult, postResult, entry.getValue());
|
||||
}
|
||||
}
|
||||
result.add(value);
|
||||
postResult.add(value);
|
||||
}
|
||||
|
||||
private List<Value> recursiveWalk(Value root) {
|
||||
List<Value> result = new ArrayList<>();
|
||||
walk(result, root);
|
||||
return result;
|
||||
private Tuple2<List<Value>, List<Value>> recursiveWalk(Value root) {
|
||||
List<Value> preResult = new ArrayList<>();
|
||||
List<Value> postResult = new ArrayList<>();
|
||||
walk(preResult, postResult, root);
|
||||
return new Tuple2<>(preResult, postResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +81,8 @@ public class ValueWalkerTest {
|
||||
Value v = JSONParser.newInstance().parse(JSONTest.getTestSource("/test.json"));
|
||||
TestVisitor visitor = new TestVisitor();
|
||||
ValueWalker.walk(v, visitor);
|
||||
Assert.assertFalse(visitor.values.isEmpty());
|
||||
Assert.assertEquals(recursiveWalk(v), visitor.values);
|
||||
Assert.assertFalse(visitor.preValues.isEmpty());
|
||||
Assert.assertFalse(visitor.postValues.isEmpty());
|
||||
Assert.assertEquals(recursiveWalk(v), new Tuple2<>(visitor.preValues, visitor.postValues));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user