added traversal library
fixed bug with missing .equals method implementation on net.woggioni.worth.value.ObjectEntry fixed multithreaded testing
This commit is contained in:
@@ -116,7 +116,6 @@ public class JBONDumper extends ValueDumper {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void endObject() {
|
||||
}
|
||||
|
||||
@@ -134,7 +133,6 @@ public class JBONDumper extends ValueDumper {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void endArray() {
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.woggioni.worth.value.ArrayValue;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ArrayStackElement extends StackElement {
|
||||
|
||||
@Getter
|
||||
private final ArrayValue value;
|
||||
|
||||
private final Iterator<Value> it;
|
||||
|
||||
@Getter
|
||||
private int currentIndex;
|
||||
|
||||
private Value currentValue = null;
|
||||
|
||||
ArrayStackElement(ArrayValue av) {
|
||||
this.value = av;
|
||||
it = av.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
Value current() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
Value next() {
|
||||
currentValue = it.next();
|
||||
currentIndex++;
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.woggioni.worth.value.ObjectValue;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class ObjectStackElement extends StackElement {
|
||||
|
||||
private final Iterator<Map.Entry<String, Value>> it;
|
||||
|
||||
@Getter
|
||||
private final ObjectValue value;
|
||||
|
||||
@Getter
|
||||
private int currentIndex;
|
||||
|
||||
@Getter
|
||||
private String currentKey;
|
||||
|
||||
private Value currentValue;
|
||||
|
||||
public ObjectStackElement(ObjectValue ov) {
|
||||
value = ov;
|
||||
it = ov.iterator();
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
Value current() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
Value next() {
|
||||
Map.Entry<String, Value> result = it.next();
|
||||
currentKey = result.getKey();
|
||||
currentIndex++;
|
||||
currentValue = result.getValue();
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
abstract class StackElement {
|
||||
abstract Value current();
|
||||
abstract Value next();
|
||||
abstract boolean hasNext();
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TraversalContext {
|
||||
Value getRoot();
|
||||
List<StackElement> getStack();
|
||||
String getPath();
|
||||
}
|
14
src/main/java/net/woggioni/worth/traversal/ValueVisitor.java
Normal file
14
src/main/java/net/woggioni/worth/traversal/ValueVisitor.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.value.*;
|
||||
|
||||
public interface ValueVisitor {
|
||||
|
||||
void visit(ObjectValue value, TraversalContext ctx);
|
||||
void visit(ArrayValue value, TraversalContext ctx);
|
||||
void visit(BooleanValue value, TraversalContext ctx);
|
||||
void visit(StringValue value, TraversalContext ctx);
|
||||
void visit(IntegerValue value, TraversalContext ctx);
|
||||
void visit(FloatValue value, TraversalContext ctx);
|
||||
void visit(NullValue value, TraversalContext ctx);
|
||||
}
|
153
src/main/java/net/woggioni/worth/traversal/ValueWalker.java
Normal file
153
src/main/java/net/woggioni/worth/traversal/ValueWalker.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package net.woggioni.worth.traversal;
|
||||
|
||||
import net.woggioni.worth.value.*;
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static net.woggioni.worth.utils.WorthUtils.dynamicCast;
|
||||
|
||||
public class ValueWalker {
|
||||
|
||||
private Value parent;
|
||||
|
||||
public ValueWalker(Value root) {
|
||||
parent = root;
|
||||
}
|
||||
|
||||
public ValueWalker get(String key) {
|
||||
if(parent.type() == Value.Type.OBJECT) {
|
||||
parent = parent.get(key);
|
||||
} else {
|
||||
parent = Value.Null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValueWalker get(int index) {
|
||||
if(parent.type() == Value.Type.ARRAY) {
|
||||
parent = parent.get(index);
|
||||
} else {
|
||||
parent = Value.Null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Value get() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public <T> Optional<T> map(Function<Value, T> callback) {
|
||||
if(isPresent()) {
|
||||
return Optional.of(callback.apply(parent));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> Optional<T> flatMap(Function<Value, Optional<T>> callback) {
|
||||
if(isPresent()) {
|
||||
return callback.apply(parent);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
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((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 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
package net.woggioni.worth.utils;
|
||||
|
||||
import net.woggioni.worth.xface.Value;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ValueWalker {
|
||||
|
||||
private Value parent;
|
||||
|
||||
public ValueWalker(Value root) {
|
||||
parent = root;
|
||||
}
|
||||
|
||||
public ValueWalker get(String key) {
|
||||
if(parent.type() == Value.Type.OBJECT) {
|
||||
parent = parent.get(key);
|
||||
} else {
|
||||
parent = Value.Null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValueWalker get(int index) {
|
||||
if(parent.type() == Value.Type.ARRAY) {
|
||||
parent = parent.get(index);
|
||||
} else {
|
||||
parent = Value.Null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Value get() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public <T> Optional<T> map(Function<Value, T> callback) {
|
||||
if(isPresent()) {
|
||||
return Optional.of(callback.apply(parent));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> Optional<T> flatMap(Function<Value, Optional<T>> callback) {
|
||||
if(isPresent()) {
|
||||
return callback.apply(parent);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return parent.type() == Value.Type.NULL;
|
||||
}
|
||||
}
|
@@ -47,6 +47,7 @@ public interface ObjectValue extends Value, Iterable<Map.Entry<String, Value>> {
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
final class ObjectEntry<K, V> implements Map.Entry<K, V> {
|
||||
private final K key;
|
||||
private V value;
|
||||
@@ -130,7 +131,7 @@ abstract class MapObjectValue implements ObjectValue {
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
class HashMapObjectValue extends MapObjectValue {
|
||||
|
||||
public HashMapObjectValue() {
|
||||
@@ -138,7 +139,7 @@ class HashMapObjectValue extends MapObjectValue {
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
class LinkedHashMapObjectValue extends MapObjectValue {
|
||||
|
||||
public LinkedHashMapObjectValue() {
|
||||
@@ -146,7 +147,7 @@ class LinkedHashMapObjectValue extends MapObjectValue {
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
class TreeMapObjectValue extends MapObjectValue {
|
||||
|
||||
public TreeMapObjectValue() {
|
||||
@@ -159,6 +160,7 @@ class TreeMapObjectValue extends MapObjectValue {
|
||||
@EqualsAndHashCode
|
||||
class ListObjectValue implements ObjectValue {
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
private final List<Map.Entry<String, Value>> value = new ArrayList<>();
|
||||
|
||||
public ListObjectValue(Map<String, Value> map) {
|
||||
|
Reference in New Issue
Block a user