package edu.umd.cs.findbugs.plugin.eclipse.quickfix;
import static edu.umd.cs.findbugs.plugin.eclipse.quickfix.util.ASTUtil.getMethodDeclaration;
import static edu.umd.cs.findbugs.plugin.eclipse.quickfix.util.ASTUtil.getTypeDeclaration;
import java.util.Iterator;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.plugin.eclipse.quickfix.exception.BugResolutionException;
/**
* Returning a reference to a mutable object is not recommended. The class
* <CODE>CreateMutableCloneResolution</CODE> returns a new copy of the object.
*
* @see <a href="http://findbugs.sourceforge.net/bugDescriptions.html#EI_EXPOSE_REP">EI_EXPOSE_REP</a>
*/
public class CreateMutableCloneResolution extends BugResolution {
@Override
public boolean resolveBindings() {
return true;
}
@Override
protected void repairBug(ASTRewrite rewrite, CompilationUnit workingUnit, BugInstance bug) throws BugResolutionException {
assert rewrite != null;
assert workingUnit != null;
assert bug != null;
TypeDeclaration type = getTypeDeclaration(workingUnit, bug.getPrimaryClass());
MethodDeclaration method = getMethodDeclaration(type, bug.getPrimaryMethod());
String fieldName = bug.getPrimaryField().getFieldName();
Expression retEx = null;
CastExpression castRet = null;
Expression original = null;
Expression cloneField;
MethodInvocation cloneInvoke;
SimpleName cloneName;
Iterator<?> itr = method.getBody().statements().iterator();
while (itr.hasNext() && original == null) {
Statement stmt = (Statement) itr.next();
if (!(stmt instanceof ReturnStatement)) {
continue;
}
retEx = ((ReturnStatement) stmt).getExpression();
if (retEx instanceof SimpleName && ((SimpleName) retEx).getIdentifier().equals(fieldName)) {
original = retEx;
} else if (retEx instanceof FieldAccess && isThisFieldAccess((FieldAccess) retEx, fieldName)) {
original = ((FieldAccess) retEx).getName();
}
}
if (original == null) {
throw new BugResolutionException("No original field found.");
}
// set up the clone part
cloneInvoke = workingUnit.getAST().newMethodInvocation();
cloneField = (SimpleName) ASTNode.copySubtree(cloneInvoke.getAST(), original);
cloneName = workingUnit.getAST().newSimpleName("clone");
cloneInvoke.setExpression(cloneField);
cloneInvoke.setName(cloneName);
// cast the result to the right type
Type retType;
castRet = workingUnit.getAST().newCastExpression();
retType = (Type) ASTNode.copySubtree(castRet.getAST(), method.getReturnType2());
castRet.setExpression(cloneInvoke);
castRet.setType(retType);
rewrite.replace(original, castRet, null);
}
private boolean isThisFieldAccess(FieldAccess access, String fieldName) {
return (access.getExpression() instanceof ThisExpression) && access.getName().getIdentifier().equals(fieldName);
}
}