
xyz.cofe.coll.im.htree.HTree Maven / Gradle / Ivy
package xyz.cofe.coll.im.htree;
import xyz.cofe.coll.im.ImList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* Обход и обновление гетерогенных деревьев (record)
*
* Работает со следующими вложенными структурами:
*
* - record
* - ImList
* - Optional
*
*
*
* Системное свойство: HTree.visitorCacheEnabled
, по умолчанию false
- указывает,
* кешировать или нет информацию о методах визитера
*
*
* В общем случае выглядит так
*
*
* HTree.visit(root, new Object() {
* // обновление конкретного типа узла
* NodeB node(NodeB n) {
* return new NodeB(n.a + n.a);
* }
*
* // обновление обобщенного типа узла
* Node node2(Node n) {
* if (n instanceof NodeC nc && nc.b==4) {
* return new NodeC(44, nc.c);
* }
* return n;
* }
*
* // вход в узел
* void enter(ImList<Nest.PathNode> path) {
* System.out.println("enter " + ">>> ".repeat(path.size()) + path.head().get());
* }
*
* // выход из узла
* void show(ImList<Nest.PathNode> path) {
* System.out.println("exit " + ">>> ".repeat(path.size()) + path.head().get());
* }
* })
*
*/
public class HTree {
private static Optional nestOf(Object root) {
if (root == null) return Optional.empty();
Class> klass = root.getClass();
if (klass.isRecord()) {
return Optional.of(new RecordNest());
}
if (root instanceof ImList>) {
return Optional.of(new ImListNest());
}
if (root instanceof Optional>) {
return Optional.of(new OptionalNest());
}
return Optional.empty();
}
private final static Map, NodeVisitor> visitorCache = new HashMap<>();
/**
* Обход дерева и обновление узлов
*
* @param root корень дерева
* @param visitor объект для посещения и обновления узлов {@link NodeVisitor}
* @param тип дерева
* @return обновленное или старое дерево
*/
public static A visit(A root, Object visitor) {
if (root == null) throw new IllegalArgumentException("root==null");
if (visitor == null) throw new IllegalArgumentException("visitor==null");
boolean cacheEnabled = System.getProperty("HTree.visitorCacheEnabled", "false").equalsIgnoreCase("true");
NodeVisitor nv = cacheEnabled
? visitorCache.computeIfAbsent(visitor.getClass(), x -> new NodeVisitor(visitor))
: new NodeVisitor(visitor);
if (cacheEnabled) nv.setVisitor(visitor);
return visit(root, ImList.of(new Nest.RootPathNode(root)), nv);
}
private static UpdateResult update(ImList path, NodeVisitor nodeVisitor) {
return nodeVisitor.update(path);
}
private static A visit(A root, ImList path, NodeVisitor nodeVisitor) {
if (path == null) throw new IllegalArgumentException("path==null");
if (path.size() < 1) throw new IllegalArgumentException("path.size() < 1");
nodeVisitor.enter(path);
var nestOpt = nestOf(root);
if (nestOpt.isEmpty()) {
// случай лист - терминальный узел
var updateResult = update(path, nodeVisitor);
if (updateResult == UpdateResult.NoUpdate.instance) {
nodeVisitor.exit(path);
return root;
} else if (updateResult instanceof UpdateResult.Updated up) {
var a = (A) up.result();
var fpath = path;
path.head().ifPresent(h -> {
nodeVisitor.exit(
fpath.tail().prepend(h.withPathValue(a))
);
});
return a;
}
}
var nest = nestOpt.get();
var iter = nest.enter(root);
var result = root;
while (true) {
var itm = iter.next();
if (itm instanceof Nest.NestItValue nv) {
var maybeUpdated = visit(nv.value(), path.prepend(nv), nodeVisitor);
if (maybeUpdated != nv.value()) {
// обновлен лист/узел
nv.update(maybeUpdated);
}
} else if (itm instanceof Nest.NestFinish nf) {
result = (A) nf.exit();
path = path.tail().prepend(path.head().get().withPathValue(result));
break;
}
}
var updateResult = update(path, nodeVisitor);
if (updateResult == UpdateResult.NoUpdate.instance) {
var aResult = result;
var fpath = path;
path.head().ifPresent(h -> {
nodeVisitor.exit(
fpath.tail().prepend(h.withPathValue(aResult))
);
});
return result;
} else if (updateResult instanceof UpdateResult.Updated up) {
var aResult = (A) up.result();
var fpath = path;
path.head().ifPresent(h -> {
nodeVisitor.exit(
fpath.tail().prepend(h.withPathValue(aResult))
);
});
return aResult;
}
var aResult = result;
var fpath = path;
path.head().ifPresent(h -> {
nodeVisitor.exit(
fpath.tail().prepend(h.withPathValue(aResult))
);
});
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy