blob: 2bc5c9b0d78c7c066c7d08c1ca226b7a56f3b7ac [file] [log] [blame]
/*
* Licensed Materials - Property of IBM,
* WebSphere Studio Workbench
* (c) Copyright IBM Corp 1999, 2000, 2001
*/
package org.eclipse.jdt.core.refactoring.code;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.refactoring.RefactoringStatus;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.Reference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
/* package */ class LocalVariableAnalyzer {
private StatementAnalyzer fStatementAnalyzer;
// Reads and writes to locals inside the selected statement block.
private List fSelectedLocalWrites= new ArrayList(2);
private List fSelectedLocalReads= new ArrayList(2);
// Reads and writes in the same method which occur after the
// selected statements.
private List fFollowingLocalWrites= new ArrayList(2);
private List fFollowingLocalReads= new ArrayList(2);
private String fLocalDeclaration= null;
private String fLhs= "";
private String fReturnStatement;
private String fReturnType= "void";
private List fUsedLocals= new ArrayList(2);
public LocalVariableAnalyzer(StatementAnalyzer analyzer) {
fStatementAnalyzer= analyzer;
}
//---- Analyzing statements ----------------------------------------------------------------
public void visit(SingleNameReference singleNameReference, BlockScope scope, int mode) {
if (isOfInterestForRead(mode))
processLocalVariableBinding(getLocalVariableBindingIfSingleNameReference(singleNameReference), mode);
}
public void visit(QualifiedNameReference reference, BlockScope scope, int mode) {
if (isOfInterestForRead(mode))
processLocalVariableBinding(getLocalVariableBindingIfQualifiedNameReference(reference), mode);
}
public void visitAssignment(Assignment assignment, BlockScope scope, int mode) {
if (isOfInterestForWrite(mode)) {
LocalVariableBinding binding= getLocalVariableBindingIfSingleNameReference(assignment.lhs);
if (binding != null) {
addLocalWrite(binding, mode);
}
}
}
public void visitPostfixPrefixExpression(Assignment assignment, BlockScope scope, int mode) {
if (isOfInterestForWrite(mode) || isOfInterestForRead(mode)) {
LocalVariableBinding binding= getLocalVariableBindingIfSingleNameReference(assignment.lhs);
if (binding != null) {
addLocalWrite(binding, mode);
addLocalRead(binding, mode);
}
}
}
private LocalVariableBinding getLocalVariableBindingIfSingleNameReference(Reference ref) {
if (ref instanceof SingleNameReference &&
((SingleNameReference)ref).binding instanceof LocalVariableBinding) {
return (LocalVariableBinding)((SingleNameReference)ref).binding;
}
return null;
}
private LocalVariableBinding getLocalVariableBindingIfQualifiedNameReference(Reference ref) {
if (ref instanceof QualifiedNameReference &&
((QualifiedNameReference)ref).binding instanceof LocalVariableBinding) {
return (LocalVariableBinding)((QualifiedNameReference)ref).binding;
}
return null;
}
private void processLocalVariableBinding(LocalVariableBinding binding, int mode) {
if (binding != null) {
// A name reference is also reported by an assignment. So don't
// treat it as a read if it is already treated as a write.
if (fSelectedLocalWrites.contains(binding) || fFollowingLocalWrites.contains(binding))
return;
addLocalRead(binding, mode);
}
}
private void addLocalWrite(LocalVariableBinding binding, int mode) {
switch (mode) {
case StatementAnalyzer.SELECTED:
if (!fSelectedLocalWrites.contains(binding))
fSelectedLocalWrites.add(binding);
break;
case StatementAnalyzer.AFTER:
if (!fFollowingLocalWrites.contains(binding))
fFollowingLocalWrites.add(binding);
break;
}
}
private void addLocalRead(LocalVariableBinding binding, int mode) {
switch (mode) {
case StatementAnalyzer.SELECTED:
if (!fSelectedLocalReads.contains(binding))
fSelectedLocalReads.add(binding);
break;
case StatementAnalyzer.AFTER:
if (!fFollowingLocalReads.contains(binding))
fFollowingLocalReads.add(binding);
break;
}
}
private boolean isOfInterestForRead(int mode) {
return mode == StatementAnalyzer.SELECTED || mode == StatementAnalyzer.AFTER;
}
private boolean isOfInterestForWrite(int mode) {
return mode == StatementAnalyzer.SELECTED || mode == StatementAnalyzer.AFTER;
}
//---- Precondition checking ----------------------------------------------------------------
public void checkActivation(RefactoringStatus status) {
checkLocalWrites(status);
checkLocalReads(status);
checkFollowingLocals(status);
}
//---- Code generation ----------------------------------------------------------------------
public String getCall(String methodName) {
StringBuffer result= new StringBuffer(fLhs);
result.append(methodName);
result.append("(");
for (int i= 0; i < fUsedLocals.size(); i++) {
if (i > 0)
result.append(", ");
LocalVariableBinding binding= (LocalVariableBinding)fUsedLocals.get(i);
result.append(binding.readableName());
}
result.append(")");
return result.toString();
}
public String getCallSignature(String methodName) {
StringBuffer result= new StringBuffer(fReturnType);
result.append(" ");
result.append(methodName);
result.append("(");
for (int i= 0; i < fUsedLocals.size(); i++) {
if (i > 0)
result.append(", ");
LocalVariableBinding binding= (LocalVariableBinding)fUsedLocals.get(i);
TypeReference typeRef= binding.declaration.type;
result.append(typeRef.toStringExpression(0));
result.append(" ");
result.append(binding.readableName());
}
result.append(")");
return result.toString();
}
public String getLocalDeclaration() {
return fLocalDeclaration;
}
public String getReturnStatement() {
return fReturnStatement;
}
public String[] getParameterTypes() {
int size= fUsedLocals.size();
String[] result= new String[size];
for (int i= 0; i < size; i++) {
LocalVariableBinding binding= (LocalVariableBinding)fUsedLocals.get(i);
TypeReference typeRef= binding.declaration.type;
result[i]= Signature.createTypeSignature(typeRef.toStringExpression(0), false);
}
return result;
}
//---- Private Helper methods -------------------------------------------------------------
private void checkLocalWrites(RefactoringStatus status) {
int count= 0;
LocalDeclaration returnDeclaration= null;
Iterator iter= fSelectedLocalWrites.iterator();
while (iter.hasNext()) {
LocalVariableBinding binding= (LocalVariableBinding)iter.next();
LocalDeclaration declaration= binding.declaration;
if (!fStatementAnalyzer.isSelected(declaration)) {
count++;
if (count > 1) {
status.addError("Ambigious return value: selected block contains more than one assignment to local variable");
return;
} else {
returnDeclaration= declaration;
}
fUsedLocals.add(binding);
}
}
if (returnDeclaration != null) {
makeReturnType(returnDeclaration);
}
}
private void checkLocalReads(RefactoringStatus statue) {
for (Iterator iter= fSelectedLocalReads.iterator(); iter.hasNext();) {
LocalVariableBinding binding= (LocalVariableBinding)iter.next();
if (!fStatementAnalyzer.isSelected(binding.declaration) && !fUsedLocals.contains(binding)) {
fUsedLocals.add(binding);
}
}
}
private void checkFollowingLocals(RefactoringStatus status) {
List followingLocals= new ArrayList(10);
followingLocals.addAll(fFollowingLocalReads);
followingLocals.addAll(fFollowingLocalWrites);
int count= 0;
LocalDeclaration returnDeclaration= null;
Iterator iter= followingLocals.iterator();
while (iter.hasNext()) {
LocalVariableBinding binding= (LocalVariableBinding)iter.next();
LocalDeclaration declaration= binding.declaration;
if (fStatementAnalyzer.isSelected(declaration)) {
count++;
if (count > 1) {
status.addError("Ambigious return value: more than one reference to selected local declaration found");
return;
} else {
returnDeclaration= declaration;
}
}
}
if (returnDeclaration != null && ! returnTypeIsVoid()) {
status.addError("Ambigious return value: assignment to local variable and reference to a selected local declaration found");
return;
}
if (returnDeclaration != null) {
makeReturnType(returnDeclaration);
makeLocalDeclaration(returnDeclaration);
}
}
private boolean returnTypeIsVoid() {
return "void".equals(fReturnType);
}
private void makeReturnType(LocalDeclaration declaration) {
TypeReference typeRef= declaration.type;
fReturnType= typeRef.toStringExpression(0);
fLhs= declaration.name() + "= ";
fReturnStatement= "return " + declaration.name() + ";";
}
private void makeLocalDeclaration(LocalDeclaration declaration) {
TypeReference typeRef= declaration.type;
fLocalDeclaration= typeRef.toStringExpression(0) + " " + declaration.name() + ";";
}
private List computeSelectedLocals() {
List result= new ArrayList(fSelectedLocalReads.size());
result.addAll(fSelectedLocalReads);
Iterator iter= fSelectedLocalWrites.iterator();
while(iter.hasNext()) {
Object element= iter.next();
if (!result.contains(element))
result.add(element);
}
return result;
}
}