/*
* Copyright 2009-2016 the original author or authors.
*
* 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.codehaus.groovy.eclipse.core.search;
import java.util.PriorityQueue;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.AttributeExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
import org.codehaus.groovy.ast.expr.BooleanExpression;
import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ClosureListExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
import org.codehaus.groovy.ast.expr.FieldExpression;
import org.codehaus.groovy.ast.expr.GStringExpression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.MapExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.MethodPointerExpression;
import org.codehaus.groovy.ast.expr.NotExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.RangeExpression;
import org.codehaus.groovy.ast.expr.SpreadExpression;
import org.codehaus.groovy.ast.expr.SpreadMapExpression;
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.ast.expr.TernaryExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.AssertStatement;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.BreakStatement;
import org.codehaus.groovy.ast.stmt.CaseStatement;
import org.codehaus.groovy.ast.stmt.CatchStatement;
import org.codehaus.groovy.ast.stmt.ContinueStatement;
import org.codehaus.groovy.ast.stmt.DoWhileStatement;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.ast.stmt.IfStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.SwitchStatement;
import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
import org.codehaus.groovy.ast.stmt.ThrowStatement;
import org.codehaus.groovy.ast.stmt.TryCatchStatement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
import org.codehaus.groovy.classgen.BytecodeExpression;
/**
* Iterates through {@link ModuleNode} members in lexical order.
*
* The order is not computed lazily, rather, when the first node is asked for,
* the entire module node is walked.
*
* {@link PackageNode}s and {@link ImportNode}s are ignored.
*
* @author andrew
* @created Jan 21, 2011
*/
public class LexicalClassVisitor {
private class LexicalPrevisitor extends ClassCodeVisitorSupport {
@Override
public void visitClass(ClassNode node) {
maybeAddNode(node);
visitObjectInitializerStatements(node);
node.visitContents(this);
}
private void maybeAddNode(ASTNode node) {
if (node.getEnd() > 0) {
nodeList.add(new ComparableNode(node));
}
}
@Override
protected void visitObjectInitializerStatements(ClassNode node) {
maybeAddNode(node);
super.visitObjectInitializerStatements(node);
}
@Override
public void visitVariableExpression(VariableExpression node) {
maybeAddNode(node);
super.visitVariableExpression(node);
}
@Override
public void visitConstructor(ConstructorNode node) {
maybeAddNode(node);
super.visitConstructor(node);
}
@Override
public void visitMethod(MethodNode node) {
maybeAddNode(node);
super.visitMethod(node);
}
@Override
public void visitField(FieldNode node) {
super.visitField(node);
}
@Override
public void visitAssertStatement(AssertStatement node) {
maybeAddNode(node);
super.visitAssertStatement(node);
}
@Override
public void visitBlockStatement(BlockStatement node) {
maybeAddNode(node);
super.visitBlockStatement(node);
}
@Override
public void visitBreakStatement(BreakStatement node) {
maybeAddNode(node);
super.visitBreakStatement(node);
}
@Override
public void visitCaseStatement(CaseStatement node) {
maybeAddNode(node);
super.visitCaseStatement(node);
}
@Override
public void visitCatchStatement(CatchStatement node) {
maybeAddNode(node);
super.visitCatchStatement(node);
}
@Override
public void visitContinueStatement(ContinueStatement node) {
maybeAddNode(node);
super.visitContinueStatement(node);
}
@Override
public void visitDoWhileLoop(DoWhileStatement node) {
maybeAddNode(node);
super.visitDoWhileLoop(node);
}
@Override
public void visitExpressionStatement(ExpressionStatement node) {
maybeAddNode(node);
super.visitExpressionStatement(node);
}
@Override
public void visitForLoop(ForStatement node) {
maybeAddNode(node);
super.visitForLoop(node);
}
@Override
public void visitIfElse(IfStatement node) {
maybeAddNode(node);
super.visitIfElse(node);
}
@Override
public void visitReturnStatement(ReturnStatement node) {
maybeAddNode(node);
super.visitReturnStatement(node);
}
@Override
public void visitSwitch(SwitchStatement node) {
maybeAddNode(node);
super.visitSwitch(node);
}
@Override
public void visitSynchronizedStatement(SynchronizedStatement node) {
maybeAddNode(node);
super.visitSynchronizedStatement(node);
}
@Override
public void visitThrowStatement(ThrowStatement node) {
maybeAddNode(node);
super.visitThrowStatement(node);
}
@Override
public void visitTryCatchFinally(TryCatchStatement node) {
maybeAddNode(node);
super.visitTryCatchFinally(node);
}
@Override
public void visitWhileLoop(WhileStatement node) {
maybeAddNode(node);
super.visitWhileLoop(node);
}
@Override
protected void visitEmptyStatement(EmptyStatement node) {
maybeAddNode(node);
super.visitEmptyStatement(node);
}
@Override
public void visitMethodCallExpression(MethodCallExpression node) {
maybeAddNode(node);
super.visitMethodCallExpression(node);
}
@Override
public void visitStaticMethodCallExpression(StaticMethodCallExpression node) {
maybeAddNode(node);
super.visitStaticMethodCallExpression(node);
}
@Override
public void visitConstructorCallExpression(ConstructorCallExpression node) {
maybeAddNode(node);
super.visitConstructorCallExpression(node);
}
@Override
public void visitBinaryExpression(BinaryExpression node) {
maybeAddNode(node);
super.visitBinaryExpression(node);
}
@Override
public void visitTernaryExpression(TernaryExpression node) {
maybeAddNode(node);
super.visitTernaryExpression(node);
}
@Override
public void visitShortTernaryExpression(ElvisOperatorExpression node) {
maybeAddNode(node);
super.visitShortTernaryExpression(node);
}
@Override
public void visitPostfixExpression(PostfixExpression node) {
maybeAddNode(node);
super.visitPostfixExpression(node);
}
@Override
public void visitPrefixExpression(PrefixExpression node) {
maybeAddNode(node);
super.visitPrefixExpression(node);
}
@Override
public void visitBooleanExpression(BooleanExpression node) {
maybeAddNode(node);
super.visitBooleanExpression(node);
}
@Override
public void visitNotExpression(NotExpression node) {
maybeAddNode(node);
super.visitNotExpression(node);
}
@Override
public void visitClosureExpression(ClosureExpression node) {
maybeAddNode(node);
super.visitClosureExpression(node);
}
@Override
public void visitTupleExpression(TupleExpression node) {
maybeAddNode(node);
super.visitTupleExpression(node);
}
@Override
public void visitListExpression(ListExpression node) {
maybeAddNode(node);
super.visitListExpression(node);
}
@Override
public void visitArrayExpression(ArrayExpression node) {
maybeAddNode(node);
super.visitArrayExpression(node);
}
@Override
public void visitMapExpression(MapExpression node) {
maybeAddNode(node);
super.visitMapExpression(node);
}
@Override
public void visitMapEntryExpression(MapEntryExpression node) {
maybeAddNode(node);
super.visitMapEntryExpression(node);
}
@Override
public void visitRangeExpression(RangeExpression node) {
maybeAddNode(node);
super.visitRangeExpression(node);
}
@Override
public void visitSpreadExpression(SpreadExpression node) {
maybeAddNode(node);
super.visitSpreadExpression(node);
}
@Override
public void visitSpreadMapExpression(SpreadMapExpression node) {
maybeAddNode(node);
super.visitSpreadMapExpression(node);
}
@Override
public void visitMethodPointerExpression(MethodPointerExpression node) {
maybeAddNode(node);
super.visitMethodPointerExpression(node);
}
@Override
public void visitUnaryMinusExpression(UnaryMinusExpression node) {
maybeAddNode(node);
super.visitUnaryMinusExpression(node);
}
@Override
public void visitUnaryPlusExpression(UnaryPlusExpression node) {
maybeAddNode(node);
super.visitUnaryPlusExpression(node);
}
@Override
public void visitBitwiseNegationExpression(BitwiseNegationExpression node) {
maybeAddNode(node);
super.visitBitwiseNegationExpression(node);
}
@Override
public void visitCastExpression(CastExpression node) {
maybeAddNode(node);
super.visitCastExpression(node);
}
@Override
public void visitConstantExpression(ConstantExpression node) {
maybeAddNode(node);
super.visitConstantExpression(node);
}
@Override
public void visitClassExpression(ClassExpression node) {
maybeAddNode(node);
super.visitClassExpression(node);
}
@Override
public void visitDeclarationExpression(DeclarationExpression node) {
maybeAddNode(node);
super.visitDeclarationExpression(node);
}
@Override
public void visitPropertyExpression(PropertyExpression node) {
maybeAddNode(node);
super.visitPropertyExpression(node);
}
@Override
public void visitAttributeExpression(AttributeExpression node) {
maybeAddNode(node);
super.visitAttributeExpression(node);
}
@Override
public void visitFieldExpression(FieldExpression node) {
maybeAddNode(node);
super.visitFieldExpression(node);
}
@Override
public void visitGStringExpression(GStringExpression node) {
maybeAddNode(node);
super.visitGStringExpression(node);
}
@Override
public void visitArgumentlistExpression(ArgumentListExpression node) {
maybeAddNode(node);
super.visitArgumentlistExpression(node);
}
@Override
public void visitClosureListExpression(ClosureListExpression node) {
maybeAddNode(node);
super.visitClosureListExpression(node);
}
@Override
public void visitBytecodeExpression(BytecodeExpression node) {
maybeAddNode(node);
super.visitBytecodeExpression(node);
}
/**
* @param module
*/
void doVisit(ModuleNode module) {
for (ClassNode node : (Iterable<ClassNode>) module.getClasses()) {
visitClass(node);
}
}
}
private class ComparableNode implements Comparable<ComparableNode> {
final ASTNode thisNode;
ComparableNode(ASTNode thisNode) {
this.thisNode = thisNode;
}
public int compareTo(ComparableNode o) {
if (thisNode.getStart() != o.thisNode.getStart()) {
return thisNode.getStart() - o.thisNode.getStart();
} else {
// if two nodes start at the same position, but end at different
// ones, assume that the one that ends later should be visited
// first
return o.thisNode.getEnd() - thisNode.getEnd();
}
}
}
private final ModuleNode module;
private PriorityQueue<ComparableNode> nodeList;
public LexicalClassVisitor(ModuleNode module) {
super();
this.module = module;
}
/**
* @return the next lexical ASTNode to visit, or null if the visit is
* complete
*/
public ASTNode getNextNode() {
if (!hasNextNode()) {
return null;
}
return nodeList.remove().thisNode;
}
private void initialize() {
nodeList = new PriorityQueue<ComparableNode>();
LexicalPrevisitor visitor = new LexicalPrevisitor();
visitor.doVisit(module);
}
public boolean hasNextNode() {
if (nodeList == null) {
initialize();
}
return !nodeList.isEmpty();
}
public void reset() {
nodeList = null;
}
}