|
|
@@ -1,4 +1,4 @@
|
|
|
-package net.ranides.assira.reflection.impl.scanner;
|
|
|
+package net.ranides.assira.reflection.visit;
|
|
|
|
|
|
import net.ranides.assira.collection.arrays.NativeArray;
|
|
|
import net.ranides.assira.functional.special.LazyFunction;
|
|
|
@@ -8,24 +8,26 @@ import net.ranides.assira.reflection.IAttribute;
|
|
|
import net.ranides.assira.reflection.IClass;
|
|
|
import net.ranides.assira.reflection.IElement;
|
|
|
import net.ranides.assira.reflection.IField;
|
|
|
-import net.ranides.assira.reflection.ObjectScanner;
|
|
|
+import net.ranides.assira.reflection.visit.WalkerContexts.ArrayContext;
|
|
|
+import net.ranides.assira.reflection.visit.WalkerContexts.CollectionContext;
|
|
|
+import net.ranides.assira.reflection.visit.WalkerContexts.ListContext;
|
|
|
+import net.ranides.assira.reflection.visit.WalkerContexts.MapContext;
|
|
|
+import net.ranides.assira.reflection.visit.WalkerContexts.ObjectContext;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
import java.util.Comparator;
|
|
|
import java.util.IdentityHashMap;
|
|
|
import java.util.Iterator;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Optional;
|
|
|
import java.util.Set;
|
|
|
+import java.util.SortedMap;
|
|
|
+import java.util.function.Predicate;
|
|
|
|
|
|
-public class ScanWalker {
|
|
|
-
|
|
|
- private static final String TYPE_PATH = "/";
|
|
|
-
|
|
|
- private static final String NAME_PATH = "/";
|
|
|
-
|
|
|
- private static final String ID_PATH = ".";
|
|
|
+public class ObjectWalker {
|
|
|
|
|
|
private final Map<Object, Long> visited = new IdentityHashMap<>();
|
|
|
|
|
|
@@ -33,25 +35,41 @@ public class ScanWalker {
|
|
|
|
|
|
private final Object root;
|
|
|
|
|
|
- private final ObjectScanner.ObjectListener listener;
|
|
|
+ private final ObjectVisitor visitor;
|
|
|
|
|
|
- public ScanWalker(Object root, ObjectScanner.ObjectListener listener) {
|
|
|
+ private ObjectWalker(Object root, ObjectVisitor visitor) {
|
|
|
this.root = root;
|
|
|
- this.listener = listener;
|
|
|
+ this.visitor = visitor;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void walk(Object input, Predicate<ObjectContext> visitor) {
|
|
|
+ walk(input, "var", visitor);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void walk(Object input, ObjectVisitor visitor) {
|
|
|
+ walk(input, "var", visitor);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void walk(Object input, String root, Predicate<ObjectContext> visitor) {
|
|
|
+ walk(input, root, new ObjectVisitor.SimpleVisitor(visitor));
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void walk(Object input, String root, ObjectVisitor visitor) {
|
|
|
+ new ObjectWalker(input, visitor).start(root);
|
|
|
}
|
|
|
|
|
|
- public void walk() {
|
|
|
- context(null, null, IClass.OBJECT, root).accept(listener);
|
|
|
+ private void start(String rootname) {
|
|
|
+ context(null, rootname, IClass.OBJECT, root).accept(visitor);
|
|
|
}
|
|
|
|
|
|
- private void mark(ObjectScanner.ObjectContext ctx) {
|
|
|
+ private void mark(ObjectContext ctx) {
|
|
|
if(!ctx.isPrimitive()) {
|
|
|
visited.put(ctx.value(), ctx.id());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private ObjectScanner.ObjectContext context(ObjectScanner.ObjectContext parent, IField field) {
|
|
|
- Object value = ScanSupport.UNDEFINED;
|
|
|
+ private ObjectContext context(ObjectContext parent, IField field) {
|
|
|
+ Object value = WalkerUtils.UNDEFINED;
|
|
|
if(!parent.isNull()) {
|
|
|
try {
|
|
|
value = field.accessible().get(parent.value());
|
|
|
@@ -62,42 +80,42 @@ public class ScanWalker {
|
|
|
IClass<?> declared = field.type();
|
|
|
IClass<?> actual = IClass.typefor(value);
|
|
|
|
|
|
- if(ScanSupport.isList(declared, actual)) {
|
|
|
+ if(WalkerUtils.isList(declared, actual)) {
|
|
|
return new CList(parent, field, value);
|
|
|
}
|
|
|
- if(ScanSupport.isCollection(declared, actual)) {
|
|
|
+ if(WalkerUtils.isCollection(declared, actual)) {
|
|
|
return new CCollection(parent, field, value);
|
|
|
}
|
|
|
- if(ScanSupport.isMap(declared, actual)) {
|
|
|
+ if(WalkerUtils.isMap(declared, actual)) {
|
|
|
return new CMap(parent, field, value);
|
|
|
}
|
|
|
- if(ScanSupport.isArray(declared, actual)) {
|
|
|
+ if(WalkerUtils.isArray(declared, actual)) {
|
|
|
return new CArray(parent, field, value);
|
|
|
}
|
|
|
return new CObject(parent, field, value);
|
|
|
}
|
|
|
|
|
|
- private ObjectScanner.ObjectContext context(ObjectScanner.ObjectContext parent, String name, IClass<?> declared, Object value) {
|
|
|
+ private ObjectContext context(ObjectContext parent, String name, IClass<?> declared, Object value) {
|
|
|
IClass<?> actual = IClass.typefor(value);
|
|
|
|
|
|
- if(ScanSupport.isList(declared, actual)) {
|
|
|
+ if(WalkerUtils.isList(declared, actual)) {
|
|
|
return new CList(parent, name, declared, value);
|
|
|
}
|
|
|
- if(ScanSupport.isCollection(declared, actual)) {
|
|
|
+ if(WalkerUtils.isCollection(declared, actual)) {
|
|
|
return new CCollection(parent, name, declared, value);
|
|
|
}
|
|
|
- if(ScanSupport.isMap(declared, actual)) {
|
|
|
+ if(WalkerUtils.isMap(declared, actual)) {
|
|
|
return new CMap(parent, name, declared, value);
|
|
|
}
|
|
|
- if(ScanSupport.isArray(declared, actual)) {
|
|
|
+ if(WalkerUtils.isArray(declared, actual)) {
|
|
|
return new CArray(parent, name, declared, value);
|
|
|
}
|
|
|
return new CObject(parent, name, declared, value);
|
|
|
}
|
|
|
|
|
|
|
|
|
- private abstract class CAbstract implements ObjectScanner.ObjectContext {
|
|
|
- private final ObjectScanner.ObjectContext parent;
|
|
|
+ private abstract class CAbstract implements ObjectContext {
|
|
|
+ private final ObjectContext parent;
|
|
|
private final String name;
|
|
|
private final IClass<?> declared;
|
|
|
private final IClass<?> actual;
|
|
|
@@ -110,7 +128,7 @@ public class ScanWalker {
|
|
|
private final LazyFunction<String> namePath = LazyReference.concurrent();
|
|
|
private final LazyFunction<String> idPath = LazyReference.concurrent();
|
|
|
|
|
|
- public CAbstract(ObjectScanner.ObjectContext parent, IField field, Object value) {
|
|
|
+ public CAbstract(ObjectContext parent, IField field, Object value) {
|
|
|
this.field = Optional.of(field);
|
|
|
this.parent = parent;
|
|
|
this.name = field.name();
|
|
|
@@ -121,7 +139,7 @@ public class ScanWalker {
|
|
|
|
|
|
}
|
|
|
|
|
|
- public CAbstract(ObjectScanner.ObjectContext parent, String name, IClass<?> declared, Object value) {
|
|
|
+ public CAbstract(ObjectContext parent, String name, IClass<?> declared, Object value) {
|
|
|
this.field = Optional.empty();
|
|
|
this.parent = parent;
|
|
|
this.name = name;
|
|
|
@@ -132,7 +150,7 @@ public class ScanWalker {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public ObjectScanner.ObjectContext parent() {
|
|
|
+ public ObjectContext parent() {
|
|
|
return parent;
|
|
|
}
|
|
|
|
|
|
@@ -173,25 +191,17 @@ public class ScanWalker {
|
|
|
|
|
|
@Override
|
|
|
public String typePath() {
|
|
|
- return typePath.apply(() -> isRoot() ? typeName() : parent.typePath() + TYPE_PATH + typeName());
|
|
|
+ return typePath.apply(() -> WalkerUtils.typePath(this));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String namePath() {
|
|
|
- return namePath.apply(() -> {
|
|
|
- if(parent != null) {
|
|
|
- String head = parent.namePath();
|
|
|
- if(head != null) {
|
|
|
- return head + NAME_PATH + name();
|
|
|
- }
|
|
|
- }
|
|
|
- return name();
|
|
|
- });
|
|
|
+ return namePath.apply(() -> WalkerUtils.namePath(this));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String idPath() {
|
|
|
- return idPath.apply(() -> isRoot() ? String.valueOf(id()) : parent.idPath() + ID_PATH + id());
|
|
|
+ return idPath.apply(() -> WalkerUtils.idPath(this));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -211,7 +221,7 @@ public class ScanWalker {
|
|
|
|
|
|
@Override
|
|
|
public boolean isDefined() {
|
|
|
- return value == ScanSupport.UNDEFINED;
|
|
|
+ return value == WalkerUtils.UNDEFINED;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -231,19 +241,19 @@ public class ScanWalker {
|
|
|
|
|
|
}
|
|
|
|
|
|
- private class CObject extends CAbstract implements ObjectScanner.ObjectContext {
|
|
|
- public CObject(ObjectScanner.ObjectContext parent, IField field, Object value) {
|
|
|
+ private class CObject extends CAbstract implements ObjectContext {
|
|
|
+ public CObject(ObjectContext parent, IField field, Object value) {
|
|
|
super(parent, field, value);
|
|
|
}
|
|
|
|
|
|
- public CObject(ObjectScanner.ObjectContext parent, String name, IClass<?> declared, Object value) {
|
|
|
+ public CObject(ObjectContext parent, String name, IClass<?> declared, Object value) {
|
|
|
super(parent, name, declared, value);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void accept(ObjectScanner.ObjectListener listener) {
|
|
|
+ public void accept(ObjectVisitor visitor) {
|
|
|
try {
|
|
|
- if (!listener.enterObject(this)) {
|
|
|
+ if (!visitor.visitObject(this)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -255,26 +265,25 @@ public class ScanWalker {
|
|
|
.stream()
|
|
|
.sort(Comparator.comparing(IElement::name))
|
|
|
.map(field -> context(this, field))
|
|
|
- .each(context -> context.accept(listener));
|
|
|
+ .each(context -> context.accept(visitor));
|
|
|
}
|
|
|
} finally {
|
|
|
mark(this);
|
|
|
- listener.leaveObject(this);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- private class CArray extends CAbstract implements ObjectScanner.ArrayContext {
|
|
|
+ private class CArray extends CAbstract implements ArrayContext {
|
|
|
|
|
|
private final NativeArray array;
|
|
|
|
|
|
- public CArray(ObjectScanner.ObjectContext parent, IField field, Object value) {
|
|
|
+ public CArray(ObjectContext parent, IField field, Object value) {
|
|
|
super(parent, field, value);
|
|
|
this.array = NativeArray.wrap(value());
|
|
|
}
|
|
|
|
|
|
- public CArray(ObjectScanner.ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
+ public CArray(ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
super(parent, name, type, value);
|
|
|
this.array = NativeArray.wrap(value());
|
|
|
}
|
|
|
@@ -295,35 +304,34 @@ public class ScanWalker {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void accept(ObjectScanner.ObjectListener listener) {
|
|
|
+ public void accept(ObjectVisitor visitor) {
|
|
|
try {
|
|
|
- if (!listener.enterArray(this)) {
|
|
|
+ if (!visitor.visitArray(this)) {
|
|
|
return;
|
|
|
}
|
|
|
if (!isNull()) {
|
|
|
IClass<?> cmp = component();
|
|
|
for (int index = 0, n = size(); index < n; index++) {
|
|
|
- context(this, "[" + index + "]", cmp, at(index)).accept(listener);
|
|
|
+ context(this, "[" + index + "]", cmp, at(index)).accept(visitor);
|
|
|
}
|
|
|
}
|
|
|
} finally {
|
|
|
mark(this);
|
|
|
- listener.leaveArray(this);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private class CList extends CAbstract implements ObjectScanner.ListContext {
|
|
|
+ private class CList extends CAbstract implements ListContext {
|
|
|
|
|
|
private final List<?> list;
|
|
|
private final LazyFunction<IClass<?>> component = LazyReference.concurrent();
|
|
|
|
|
|
- public CList(ObjectScanner.ObjectContext parent, IField field, Object value) {
|
|
|
+ public CList(ObjectContext parent, IField field, Object value) {
|
|
|
super(parent, field, value);
|
|
|
this.list = (List<?>)value();
|
|
|
}
|
|
|
|
|
|
- public CList(ObjectScanner.ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
+ public CList(ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
super(parent, name, type, value);
|
|
|
this.list = (List<?>)value();
|
|
|
}
|
|
|
@@ -336,10 +344,10 @@ public class ScanWalker {
|
|
|
@Override
|
|
|
public IClass<?> component() {
|
|
|
return component.apply(() -> {
|
|
|
- if(ScanSupport.isList(type())) {
|
|
|
+ if(WalkerUtils.isList(type())) {
|
|
|
return type().params().get(0);
|
|
|
}
|
|
|
- if(ScanSupport.isList(actual())) {
|
|
|
+ if(WalkerUtils.isList(actual())) {
|
|
|
return actual().params().get(0);
|
|
|
}
|
|
|
return IClass.OBJECT;
|
|
|
@@ -357,35 +365,34 @@ public class ScanWalker {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void accept(ObjectScanner.ObjectListener listener) {
|
|
|
+ public void accept(ObjectVisitor visitor) {
|
|
|
try {
|
|
|
- if (!listener.enterList(this)) {
|
|
|
+ if (!visitor.visitList(this)) {
|
|
|
return;
|
|
|
}
|
|
|
if (!isNull()) {
|
|
|
IClass<?> cmp = component();
|
|
|
for (int index = 0, n = size(); index < n; index++) {
|
|
|
- context(this, "[" + index + "]", cmp, at(index)).accept(listener);
|
|
|
+ context(this, "[" + index + "]", cmp, at(index)).accept(visitor);
|
|
|
}
|
|
|
}
|
|
|
} finally {
|
|
|
mark(this);
|
|
|
- listener.leaveList(this);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private class CCollection extends CAbstract implements ObjectScanner.CollectionContext {
|
|
|
+ private class CCollection extends CAbstract implements CollectionContext {
|
|
|
|
|
|
private final Collection<?> collection;
|
|
|
private final LazyFunction<IClass<?>> component = LazyReference.concurrent();
|
|
|
|
|
|
- public CCollection(ObjectScanner.ObjectContext parent, IField field, Object value) {
|
|
|
+ public CCollection(ObjectContext parent, IField field, Object value) {
|
|
|
super(parent, field, value);
|
|
|
this.collection = (Collection<?>)value();
|
|
|
}
|
|
|
|
|
|
- public CCollection(ObjectScanner.ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
+ public CCollection(ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
super(parent, name, type, value);
|
|
|
this.collection = (Collection<?>)value();
|
|
|
}
|
|
|
@@ -398,10 +405,10 @@ public class ScanWalker {
|
|
|
@Override
|
|
|
public IClass<?> component() {
|
|
|
return component.apply(() -> {
|
|
|
- if(ScanSupport.isCollection(type())) {
|
|
|
+ if(WalkerUtils.isCollection(type())) {
|
|
|
return type().params().get(0);
|
|
|
}
|
|
|
- if(ScanSupport.isCollection(actual())) {
|
|
|
+ if(WalkerUtils.isCollection(actual())) {
|
|
|
return actual().params().get(0);
|
|
|
}
|
|
|
return IClass.OBJECT;
|
|
|
@@ -419,9 +426,9 @@ public class ScanWalker {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void accept(ObjectScanner.ObjectListener listener) {
|
|
|
+ public void accept(ObjectVisitor listener) {
|
|
|
try {
|
|
|
- if (!listener.enterCollection(this)) {
|
|
|
+ if (!listener.visitCollection(this)) {
|
|
|
return;
|
|
|
}
|
|
|
if (!isNull()) {
|
|
|
@@ -432,23 +439,22 @@ public class ScanWalker {
|
|
|
}
|
|
|
} finally {
|
|
|
mark(this);
|
|
|
- listener.leaveCollection(this);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private class CMap extends CAbstract implements ObjectScanner.MapContext {
|
|
|
+ private class CMap extends CAbstract implements MapContext {
|
|
|
|
|
|
private final Map<?,?> map;
|
|
|
private final LazyFunction<IClass<?>> keyComponent = LazyReference.concurrent();
|
|
|
private final LazyFunction<IClass<?>> valueComponent = LazyReference.concurrent();
|
|
|
|
|
|
- public CMap(ObjectScanner.ObjectContext parent, IField field, Object value) {
|
|
|
+ public CMap(ObjectContext parent, IField field, Object value) {
|
|
|
super(parent, field, value);
|
|
|
this.map = (Map<?,?>)value();
|
|
|
}
|
|
|
|
|
|
- public CMap(ObjectScanner.ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
+ public CMap(ObjectContext parent, String name, IClass<?> type, Object value) {
|
|
|
super(parent, name, type, value);
|
|
|
this.map = (Map<?,?>)value();
|
|
|
}
|
|
|
@@ -466,10 +472,10 @@ public class ScanWalker {
|
|
|
@Override
|
|
|
public IClass<?> keyComponent() {
|
|
|
return keyComponent.apply(() -> {
|
|
|
- if(ScanSupport.isMap(type())) {
|
|
|
+ if(WalkerUtils.isMap(type())) {
|
|
|
return type().params().get(0);
|
|
|
}
|
|
|
- if(ScanSupport.isMap(actual())) {
|
|
|
+ if(WalkerUtils.isMap(actual())) {
|
|
|
return actual().params().get(0);
|
|
|
}
|
|
|
return IClass.OBJECT;
|
|
|
@@ -479,10 +485,10 @@ public class ScanWalker {
|
|
|
@Override
|
|
|
public IClass<?> valueComponent() {
|
|
|
return valueComponent.apply(() -> {
|
|
|
- if(ScanSupport.isMap(type())) {
|
|
|
+ if(WalkerUtils.isMap(type())) {
|
|
|
return type().params().get(1);
|
|
|
}
|
|
|
- if(ScanSupport.isMap(actual())) {
|
|
|
+ if(WalkerUtils.isMap(actual())) {
|
|
|
return actual().params().get(1);
|
|
|
}
|
|
|
return IClass.OBJECT;
|
|
|
@@ -495,20 +501,38 @@ public class ScanWalker {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void accept(ObjectScanner.ObjectListener listener) {
|
|
|
+ public void accept(ObjectVisitor listener) {
|
|
|
try {
|
|
|
- if (!listener.enterMap(this)) {
|
|
|
+ if (!listener.visitMap(this)) {
|
|
|
return;
|
|
|
}
|
|
|
if (!isNull()) {
|
|
|
- IClass<?> cmp = valueComponent();
|
|
|
- for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
|
- context(this, "[" + entry.getKey() + "]", cmp, entry.getValue()).accept(listener);
|
|
|
+ if(map instanceof SortedMap || map instanceof LinkedHashMap) {
|
|
|
+ exactIterate(listener);
|
|
|
+ } else {
|
|
|
+ lexicalIterate(listener);
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
} finally {
|
|
|
mark(this);
|
|
|
- listener.leaveMap(this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void exactIterate(ObjectVisitor listener) {
|
|
|
+ IClass<?> cmp = valueComponent();
|
|
|
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
|
+ context(this, "[" + entry.getKey() + "]", cmp, entry.getValue()).accept(listener);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void lexicalIterate(ObjectVisitor listener) {
|
|
|
+ IClass<?> cmp = valueComponent();
|
|
|
+ ArrayList<?> keys = new ArrayList<>(map.keySet());
|
|
|
+
|
|
|
+ keys.sort(Comparator.comparing(String::valueOf));
|
|
|
+ for(Object key : keys) {
|
|
|
+ context(this, "[" + key + "]", cmp, map.get(key)).accept(listener);
|
|
|
}
|
|
|
}
|
|
|
}
|