/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.errai.jpa.rebind;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import org.jboss.errai.common.client.api.Assert;
import antlr.collections.AST;
/**
* Facilitates iteration through the nodes of an ANTLR AST. The iteration order
* is an in-order traversal of the nodes in the tree.
*
* @author Jonathan Fuerth <jfuerth@gmail.com>
*/
public class AstInorderTraversal implements Iterator<AST> {
private final AST root;
private final Deque<AST> context = new ArrayDeque<AST>();
private AST nextNode;
/**
* Creates a new traversal of the given AST. Multiple traversers can exist
* independently on the same AST.
*
* @param ast
* The AST to traverse. Must not be null.
*/
public AstInorderTraversal(AST ast) {
this.root = Assert.notNull(ast);
nextNode = root;
}
@Override
public boolean hasNext() {
return nextNode != null;
}
@Override
public AST next() {
if (!hasNext())
throw new IllegalStateException("This traversal is done");
final AST thisNode = nextNode;
// look forward for the next next node
if (nextNode.getFirstChild() != null) {
context.push(nextNode);
nextNode = nextNode.getFirstChild();
}
else if (nextNode.getNextSibling() != null) {
nextNode = nextNode.getNextSibling();
}
else {
nextNode = null;
while (!context.isEmpty() && nextNode == null) {
AST beenThere = context.pop();
if (beenThere.getNextSibling() != null) {
nextNode = beenThere.getNextSibling();
break;
}
}
}
return thisNode;
}
/**
* Returns the stack of AST nodes that lead from the root of the AST down to
* the current node that this traverser is positioned on.
*
* @return A sequence of nodes, where the first item in the list is the root
* of the AST, the last item is the current node, and all elements
* have the relationship that the node at position {@code p} is the
* parent of the node at position {@code p + 1}.
*/
public Deque<AST> context() {
return context;
}
/**
* Advances this iterator past the subtree rooted at the given node. The
* cursor will be left in one of the following places, depending on the
* position of the node:
* <ol>
* <li>On the sibling node immediately following the given node, if such a
* node exists
* <li>On an "uncle": the next sibling of the first ancestor node that has an
* unvisited sibling, if such a node exists
* <li>At the end of the iteration ({@link #hasNext()} will return false).
* </ol>
*
* @param node
* The node to fast-forward past.
*/
public void fastForwardToNextSiblingOf(AST node) {
// phase 1: search for the requested node in the context stack
for (;;) {
if (context.isEmpty()) {
throw new IllegalArgumentException("The given node " + node
+ " was not a parent node of the starting point");
}
if (context.pop() == node) {
context.push(node);
break;
}
}
// phase 2: find the next node after the requested one, which might be a
// direct sibling or a sibling of an ancestor
nextNode = null;
while (!context.isEmpty() && nextNode == null) {
AST beenThere = context.pop();
if (beenThere.getNextSibling() != null) {
nextNode = beenThere.getNextSibling();
break;
}
}
}
public AST fastForwardTo(int nodeType) {
while (hasNext()) {
AST ast = next();
if (ast.getType() == nodeType) {
return ast;
}
}
return null;
}
public AST fastForwardTo(AST node) {
while (hasNext()) {
AST ast = next();
if (ast == node) {
return ast;
}
}
throw new IllegalArgumentException("Didn't find requested node in the remainder of the traversal");
}
/**
* Not implemented.
*
* @throws UnsupportedOperationException
* when called
*/
@Override
public void remove() {
throw new UnsupportedOperationException("Not implemented");
}
}