/*
* Copyright (C) 2007, 2010 Martin Kempf, Reto Kleeb, Michael Klenk
*
* Adapted for Groovy-Eclipse 2.0 by Andrew Eisenberg
*
* IFS Institute for Software, HSR Rapperswil, Switzerland
* http://ifs.hsr.ch/
*
* 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.refactoring.core.extract;
import java.util.LinkedHashSet;
import java.util.Set;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.DoWhileStatement;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.ast.stmt.IfStatement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
import org.codehaus.groovy.eclipse.refactoring.core.utils.ASTVisitorDecorator;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
/**
* @author Michael Klenk mklenk@hsr.ch
*
* AST Visitor, which counts all variables
* in the given node split in: - declaratedVariables Variables
* declarated in this Block - declaratedInblockVariables Variables
* declarated in an inner Block - usedVariables Variables which are read
* in this block - returnVariables Variables which are assigned to a new
* value in this Block
*/
public class ASTVariableScanner {
private Set<Variable> declaredVariables = new LinkedHashSet<Variable>();
private Set<Variable> declaredInblockVariables = new LinkedHashSet<Variable>();
private Set<Variable> usedVariables = new LinkedHashSet<Variable>();
private Set<Variable> returnVariables = new LinkedHashSet<Variable>();
private Set<Variable> innerLoopAssignedVariables = new LinkedHashSet<Variable>();
private boolean selectionIsInLoopOrClosure = false;
/**
* Constructor to initialize the Scanner
*/
public ASTVariableScanner(boolean blockIsInLoop) {
selectionIsInLoopOrClosure = blockIsInLoop;
}
/**
* @return all new declared variables in the visited node
*/
public Set<Variable> getDeclaratedVariables() {
return declaredVariables;
}
/**
* @return all read variables in the visted node
*/
public Set<Variable> getUsedVariables() {
return usedVariables;
}
/**
* @return all variables which are assigned to a new Value a =, a++, a--
*/
public Set<Variable> getAssignedVariables() {
return returnVariables;
}
/**
* @return all variables which are assigned in an innerLoop
*/
public Set<Variable> getInnerLoopAssignedVariables() {
return innerLoopAssignedVariables;
}
public void visitNode(ASTNode node) {
if (selectionIsInLoopOrClosure) {
node.visit(new RepeatableBlockVisit(this));
} else {
node.visit(new DefaultVisit(this));
}
}
private class DefaultVisit extends ASTVisitorDecorator<ASTVariableScanner> {
public DefaultVisit(ASTVariableScanner container) {
super(container);
}
@Override
public void visitVariableExpression(VariableExpression expression) {
super.visitVariableExpression(expression);
Variable var = expression.getAccessedVariable();
if (expression.getEnd() < 1) {
return;
} else if (var != null && !(var instanceof FieldNode)) {
if (!declaredVariables.contains(var)) {
usedVariables.add(var);
}
}
}
@Override
public void visitDeclarationExpression(DeclarationExpression expression) {
Variable var = expression.getVariableExpression()
.getAccessedVariable();
if (var != null) {
declaredVariables.add(var);
}
super.visitDeclarationExpression(expression);
}
@Override
public void visitForLoop(ForStatement forLoop) {
if (forLoop.getVariable() != null) {
declaredInblockVariables.add(forLoop.getVariable());
}
forLoop.getCollectionExpression().visit(
new RepeatableBlockVisit(container));
forLoop.getLoopBlock().visit(new RepeatableBlockVisit(container));
}
@Override
public void visitWhileLoop(WhileStatement loop) {
loop.getLoopBlock().visit(new RepeatableBlockVisit(container));
}
@Override
public void visitDoWhileLoop(DoWhileStatement loop) {
loop.getLoopBlock().visit(new RepeatableBlockVisit(container));
}
@Override
public void visitIfElse(IfStatement ifElse) {
ifElse.getIfBlock().visit(new InnerBlockVisit(container));
ifElse.getElseBlock().visit(new InnerBlockVisit(container));
}
@Override
public void visitClosureExpression(ClosureExpression expression) {
if (expression.getParameters().length > 0) {
for (Parameter p : expression.getParameters()) {
declaredInblockVariables.add(p);
}
}
expression.getCode().visit(new ClosureVisit(container));
}
@Override
public void visitBinaryExpression(BinaryExpression expression) {
if (expression.getLeftExpression() instanceof VariableExpression) {
expression.getLeftExpression().visit(new DefaultAssignementVisit(container, expression.getOperation()));
} else {
expression.getLeftExpression().visit(this);
}
expression.getRightExpression().visit(this);
}
@Override
public void visitPostfixExpression(PostfixExpression expression) {
expression.getExpression().visit(
new DefaultAssignementVisit(container, expression.getOperation()));
}
@Override
public void visitPrefixExpression(PrefixExpression expression) {
expression.getExpression().visit(
new DefaultAssignementVisit(container, expression.getOperation()));
}
protected boolean isUsed(Variable var) {
return declaredVariables.contains(var) || declaredInblockVariables.contains(var);
}
}
private class DefaultAssignementVisit extends DefaultVisit {
protected Token operator;
public DefaultAssignementVisit(ASTVariableScanner container, Token token) {
super(container);
this.operator = token;
}
@Override
public void visitVariableExpression(VariableExpression expression) {
super.visitVariableExpression(expression);
if (expression.getEnd() < 1) {
return;
}
Variable var = expression.getAccessedVariable();
if (/* isUsed(var) && */operator.getType() != Types.LEFT_SQUARE_BRACKET) {
returnVariables.add(var);
}
}
}
private class InnerBlockVisit extends DefaultVisit {
public InnerBlockVisit(ASTVariableScanner container) {
super(container);
}
@Override
public void visitVariableExpression(VariableExpression expression) {
if (expression.getEnd() < 1) {
return;
}
Variable var = expression.getAccessedVariable();
if (var != null && !(var instanceof FieldNode)) {
if (!declaredInblockVariables.contains(var) && !declaredVariables.contains(var)) {
usedVariables.add(var);
}
}
}
}
private class RepeatableBlockVisit extends InnerBlockVisit {
public RepeatableBlockVisit(ASTVariableScanner container) {
super(container);
}
protected void checkAssertedVariables(VariableExpression expression, int operatorTokenType) {
if(operatorTokenType == Types.LEFT_SQUARE_BRACKET)
return;
Variable var = expression.getAccessedVariable();
if (var != null && isUsed(var)) {
returnVariables.add(var);
}
if (usedVariables.contains(var)) {
innerLoopAssignedVariables.add(var);
}
}
@Override
public void visitBinaryExpression(BinaryExpression expression) {
expression.getLeftExpression().visit(new AssignmentInRepeatableBlockVisit(container, expression.getOperation()));
expression.getRightExpression().visit(new RepeatableBlockVisit(container));
}
@Override
public void visitPostfixExpression(PostfixExpression expression) {
expression.getExpression().visit(
new AssignmentInRepeatableBlockVisit(container, expression.getOperation()));
}
@Override
public void visitPrefixExpression(PrefixExpression expression) {
expression.getExpression().visit(
new AssignmentInRepeatableBlockVisit(container, expression.getOperation()));
}
@Override
public void visitIfElse(IfStatement ifElse) {
ifElse.getIfBlock().visit(this);
ifElse.getElseBlock().visit(this);
}
}
private class AssignmentInRepeatableBlockVisit extends
RepeatableBlockVisit {
protected Token operator;
public AssignmentInRepeatableBlockVisit(ASTVariableScanner container, Token operator) {
super(container);
this.operator = operator;
}
@Override
public void visitVariableExpression(VariableExpression expression) {
super.visitVariableExpression(expression);
checkAssertedVariables(expression,operator.getType());
}
}
private class ClosureVisit extends RepeatableBlockVisit {
public ClosureVisit(ASTVariableScanner container) {
super(container);
}
@Override
public void visitVariableExpression(VariableExpression expression) {
Variable var = expression.getAccessedVariable();
// implicit closure variable
if (var instanceof Parameter && ((Parameter) var).getEnd() <= 0 && var.getName().equals("it")) {
declaredInblockVariables.add(var);
}
super.visitVariableExpression(expression);
}
@Override
public void visitBinaryExpression(BinaryExpression expression) {
expression.getLeftExpression().visit(new AssignementInClosureVisit(container, expression.getOperation()));
expression.getRightExpression().visit(this);
}
@Override
public void visitPostfixExpression(PostfixExpression expression) {
expression.getExpression().visit(
new AssignementInClosureVisit(container, expression.getOperation()));
}
@Override
public void visitPrefixExpression(PrefixExpression expression) {
expression.getExpression().visit(
new AssignementInClosureVisit(container, expression.getOperation()));
}
}
private class AssignementInClosureVisit extends ClosureVisit {
protected Token operator;
public AssignementInClosureVisit(ASTVariableScanner container, Token token) {
super(container);
this.operator = token;
}
@Override
public void visitVariableExpression(VariableExpression expression) {
super.visitVariableExpression(expression);
checkAssertedVariables(expression,operator.getType());
}
}
}