package com.yoursway.rails.commons; import java.util.ArrayList; import org.eclipse.core.runtime.Assert; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.ASTVisitor; /** * Traverses a DLTK AST using {@link RubyAstVisitor}s. * * While this object does hold some internal data, it has no user-visible state. * * @author Andrey Tarantsov */ public final class RubyAstTraverser { private final ASTVisitor adapter = new ASTVisitor() { private int ignoreEnds = 0; @Override public void endvisitGeneral(ASTNode node) { if (ignoreEnds > 0) --ignoreEnds; else leave(node); } @Override public boolean visitGeneral(ASTNode node) { boolean result = enter(node); if (!result) ignoreEnds++; return result; } }; /** * Traverses the given node with the given visitor. If the visitor's enter * method returns some visitor, uses that visitor to recursively traverse * the children of the given node (and so forth). * * This method is reenterable, so it can be called from inside a visitor on * the same traverser object. * * This method provides a strong exception safety guarantee: if an exception * interrupts the traversing in progress, the object will be left in exactly * the same state as it was before calling this method. (In fact, upon * successful completion of this method the object is also left in an * unchanged state.) * * This method must be called with a newly created visitor that does not * have a parent (i.e. <code>null</code> was passed into the constructor). * * @param node * a node to traverse. * @param rootVisitor * a visitor that will visit the given node. */ public void traverse(ASTNode node, RubyAstVisitor<?> rootVisitor) { Assert.isNotNull(node); Assert.isNotNull(rootVisitor); Assert.isTrue(rootVisitor.getParentVisitor() == null); try { RubyAstVisitor<?> outerVisitor = currentVisitor; currentVisitor = rootVisitor; try { node.traverse(adapter); rootVisitor.leaveNode(null); } finally { currentVisitor = outerVisitor; } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } private RubyAstVisitor<?> currentVisitor; private boolean enter(ASTNode node) { RubyAstVisitor<?> childrenVisitor = currentVisitor.switchEnter(node); if (childrenVisitor == null) return false; childrenVisitor.$verify(currentVisitor, node); currentVisitor = childrenVisitor; return true; } private void leave(ASTNode node) { currentVisitor = currentVisitor.leaveNode(node); } }