refactor and improvement to ValueWalker.walk

This commit is contained in:
2019-09-08 20:40:18 +01:00
parent ea1bfc84c7
commit 0a9d38bdc3
12 changed files with 466 additions and 177 deletions

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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) {}
}

View File

@@ -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);
}
}
}

View 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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}