package dgm.trees2; import dgm.trees.TreeViewer; import dgm.trees.TreeVisitor; import java.util.LinkedList; public class Trees2 { private enum Token { NODE { public <A> boolean visit(A node, TreeViewer<A> viewer, TreeVisitor<A> visitor) { return visitor.visitNode(node, viewer); } }, BEGIN { public <A> boolean visit(A node, TreeViewer<A> viewer, TreeVisitor<A> visitor) { visitor.beginChildren(node, viewer); return true; } }, END { public <A> boolean visit(A node, TreeViewer<A> viewer, TreeVisitor<A> visitor) { visitor.endChildren(node, viewer); return true; } }; // return false if the Token is a "marker", ie. BEGIN or END abstract <A> boolean visit(A node, TreeViewer<A> viewer, TreeVisitor<A> visitor); } static class Event<A> { public final A node; public final Token token; public final Event<A> otherSide; private Event(Token token, A node, Event<A> other) { this.node = node; this.token = token; this.otherSide = other; } public boolean callVisitor(TreeViewer<A> viewer, TreeVisitor<A> visitor) { return token.visit(node, viewer, visitor); } static <A> Event<A> begin(A node, Event<A> other) { return new Event<A>(Token.BEGIN, node, other); } static <A> Event<A> end(A node) { return new Event<A>(Token.END, node, null); } static <A> Event<A> node(A node) { return new Event<A>(Token.NODE, node, null); } } /** * BFS visit tree. * * Suppose we have this tree. * * <pre> * * ,--(1) * / * -(0)----(2) * \ * `---(3) * * </pre> * * bfsVisit will call on {@link TreeVisitor} the following methods, in roughly this order: * * <pre> * - visit v[0] * - beginChildren v[0] * - visit v[2] * - visit v[3] * - visit v[1] * - beginChildren v[2] * - endChildren v[2] * - beginChildren v[3] * - endChildren v[3] * - beginChildren v[1] * - endChildren v[1] * - endChildren v[0] * </pre> */ public static <A> void bfsVisit(final A root, final TreeViewer<A> viewer, final TreeVisitor<A> visitor) { // algorithm as follows: // - put first node on the tree // - visit that node // - put begin on queue // visit all nodes // - put end on queue final LinkedList<Event<A>> q = new LinkedList<Event<A>>(); q.offer(Event.node(root)); Event<A> insertionPt = null; while (!q.isEmpty()) { final Event<A> a = q.poll(); // update the insertion point to the END node if (a.token == Token.BEGIN) insertionPt = a.otherSide; // call visitor interface if (a.callVisitor(viewer, visitor)) // nothing further to be done for marker event (BEGIN/END) continue; final Event<A> end = Event.end(a.node); final Event<A> start = Event.begin(a.node, end); insertBefore(q, start, insertionPt); // add children to end of the queue for (A c : viewer.children(a.node)) insertBefore(q, Event.node(c), insertionPt); insertBefore(q, end, insertionPt); } } private static <A> void insertBefore(LinkedList<A> ll, A value, A before) { if (before == null) { ll.addLast(value); return; } final int i = ll.indexOf(before); ll.add(i, value); } }