package mit.edu.concurrencyrefactorings.util;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.PrefixExpression.Operator;
import org.eclipse.jdt.internal.corext.dom.Bindings;
public class ReferencesFinder extends ASTVisitor {
List<ASTNode> readReferences;
List<ASTNode> writeReferences;
private Name nodetoSearchFor;
private IBinding targetBinding;
public List<ASTNode> getReadReferences() {
return readReferences;
}
public List<ASTNode> getWriteReferences() {
return writeReferences;
}
/**
*
* @param searchScope
* @param nodeToSearchFor
* @param startPosition inclusive
* @param endPosition Inclusive
*/
public void findReferences(ASTNode searchScope, Name nodeToSearchFor, int startPosition, int endPosition){
readReferences = new ArrayList<ASTNode>();
writeReferences = new ArrayList<ASTNode>();
this.nodetoSearchFor = nodeToSearchFor;
targetBinding = nodeToSearchFor.resolveBinding();
searchScope.accept(this);
writeReferences = filter(writeReferences, startPosition, endPosition);
readReferences = filter(readReferences, startPosition, endPosition);
}
public boolean visit(QualifiedName node) {
final IBinding binding= node.resolveBinding();
if (binding instanceof IVariableBinding) {
SimpleName name= node.getName();
return !addUsage(name, name.resolveBinding());
}
return !addUsage(node, binding);
}
public boolean visit(SimpleName node) {
addUsage(node, node.resolveBinding());
return true;
}
/*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ConstructorInvocation)
*/
public boolean visit(ClassInstanceCreation node) {
// match with the constructor and the type.
Type type= node.getType();
if (type instanceof ParameterizedType) {
type= ((ParameterizedType) type).getType();
}
if (type instanceof SimpleType) {
Name name= ((SimpleType) type).getName();
if (name instanceof QualifiedName)
name= ((QualifiedName)name).getName();
addUsage(name, node.resolveConstructorBinding());
}
return super.visit(node);
}
public boolean visit(Assignment node) {
SimpleName name= getSimpleName(node.getLeftHandSide());
if (name != null)
addWrite(name, name.resolveBinding());
return true;
}
public boolean visit(SingleVariableDeclaration node) {
addWrite(node.getName(), node.resolveBinding());
return true;
}
public boolean visit(VariableDeclarationFragment node) {
if (node.getInitializer() != null)
addWrite(node.getName(), node.resolveBinding());
return true;
}
public boolean visit(PrefixExpression node) {
PrefixExpression.Operator operator= node.getOperator();
if (operator == Operator.INCREMENT || operator == Operator.DECREMENT) {
SimpleName name= getSimpleName(node.getOperand());
if (name != null)
if (addWrite(name, name.resolveBinding())) {
readReferences.add(name);
}
}
return true;
}
public boolean visit(PostfixExpression node) {
SimpleName name= getSimpleName(node.getOperand());
if (name != null)
if (addWrite(name, name.resolveBinding())) {
readReferences.add(name);
}
return true;
}
private boolean addWrite(Name node, IBinding binding) {
if (binding != null && Bindings.equals(getBindingDeclaration(binding), targetBinding)) {
writeReferences.add(node);
return true;
}
return false;
}
private boolean addUsage(Name node, IBinding binding) {
if (binding != null && Bindings.equals(getBindingDeclaration(binding), targetBinding)) {
if (! writeReferences.contains(node)) {
//only add the current node if it does not overlap with write access
readReferences.add(node);
}
return true;
}
return false;
}
private SimpleName getSimpleName(Expression expression) {
if (expression instanceof SimpleName)
return ((SimpleName)expression);
else if (expression instanceof QualifiedName)
return (((QualifiedName) expression).getName());
else if (expression instanceof FieldAccess)
return ((FieldAccess)expression).getName();
return null;
}
private IBinding getBindingDeclaration(IBinding binding) {
switch (binding.getKind()) {
case IBinding.TYPE :
return ((ITypeBinding)binding).getTypeDeclaration();
case IBinding.METHOD :
return ((IMethodBinding)binding).getMethodDeclaration();
case IBinding.VARIABLE :
return ((IVariableBinding)binding).getVariableDeclaration();
default:
return binding;
}
}
private List<ASTNode> filter(List<ASTNode> references, int startPosition, int endPosition) {
List<ASTNode> filteredReferences = new ArrayList<ASTNode>();
for (ASTNode reference : references) {
if ((reference.getStartPosition() >= startPosition) &&
(reference.getStartPosition() <= endPosition))
filteredReferences.add(reference);
}
return filteredReferences;
}
public boolean hasReadReferences() {
return getReadReferences().size() > 0;
}
public boolean hasWriteReferences() {
return getWriteReferences().size() > 0;
}
}