package polyglot.visit; import polyglot.ast.Node; import polyglot.util.*; import java.util.*; /** * A HaltingVisitor is used to prune the traversal of the AST at a * particular node. Clients can call <code>bypass(Node n) </code> to * have the visitor skip n and its children when recursing through the AST. */ public abstract class HaltingVisitor extends NodeVisitor implements Copy { Node bypassParent; Collection bypass; public HaltingVisitor bypassChildren(Node n) { HaltingVisitor v = (HaltingVisitor) copy(); v.bypassParent = n; return v; } public HaltingVisitor visitChildren() { HaltingVisitor v = (HaltingVisitor) copy(); v.bypassParent = null; v.bypass = null; return v; } public HaltingVisitor bypass(Node n) { if (n == null) return this; HaltingVisitor v = (HaltingVisitor) copy(); // FIXME: Using a collection is expensive, but is hopefully not // often used. if (this.bypass == null) { v.bypass = Collections.singleton(n); } else { v.bypass = new ArrayList(this.bypass.size()+1); v.bypass.addAll(bypass); v.bypass.add(n); } return v; } public HaltingVisitor bypass(Collection c) { if (c == null) return this; HaltingVisitor v = (HaltingVisitor) copy(); // FIXME: Using a collection is expensive, but is hopefully not // often used. if (this.bypass == null) { v.bypass = new ArrayList(c); } else { v.bypass = new ArrayList(this.bypass.size()+c.size()); v.bypass.addAll(bypass); v.bypass.addAll(c); } return v; } public final Node override(Node parent, Node n) { if (bypassParent != null && bypassParent == parent) { // System.out.println("bypassing " + n + // " (child of " + parent + ")"); return n; } if (bypass != null) { for (Iterator i = bypass.iterator(); i.hasNext(); ) { if (i.next() == n) { // System.out.println("bypassing " + n); return n; } } } return null; } public Object copy() { try { HaltingVisitor v = (HaltingVisitor) super.clone(); // v.bypassParent = null; // v.bypass = null; return v; } catch (CloneNotSupportedException e) { throw new InternalCompilerError("Java clone() weirdness."); } } }