package quickfix;
import static edu.umd.cs.findbugs.plugin.eclipse.quickfix.util.ASTUtil.getASTNode;
import static util.TraversalUtil.backtrackToBlock;
import java.util.ArrayList;
import java.util.List;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import edu.umd.cs.findbugs.plugin.eclipse.quickfix.BugResolution;
import edu.umd.cs.findbugs.plugin.eclipse.quickfix.exception.BugResolutionException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
public class IsEmptyResolution extends BugResolution {
@Override
protected boolean resolveBindings() {
return true;
}
@Override
protected void repairBug(ASTRewrite rewrite, CompilationUnit workingUnit, BugInstance bug) throws BugResolutionException {
ASTNode node = getASTNode(workingUnit, bug.getPrimarySourceLineAnnotation());
node = backtrackToBlock(node);
IsEmptyVisitor visitor = new IsEmptyVisitor();
node.accept(visitor);
for (ResolutionBundle bundle : visitor.resolutionBundles) {
Expression newIsEmpty = makeCallToIsEmpty(rewrite, bundle);
rewrite.replace(bundle.badEqualsCheck, newIsEmpty, null);
}
}
private Expression makeCallToIsEmpty(ASTRewrite rewrite, ResolutionBundle bundle) {
AST ast = rewrite.getAST();
MethodInvocation callToEmpty = ast.newMethodInvocation();
callToEmpty.setName(ast.newSimpleName("isEmpty"));
callToEmpty.setExpression((Expression) rewrite.createCopyTarget(bundle.collectionToFix));
if (bundle.wasNegated) {
PrefixExpression negatedExpression = ast.newPrefixExpression();
negatedExpression.setOperator(PrefixExpression.Operator.NOT);
negatedExpression.setOperand(callToEmpty);
return negatedExpression;
}
return callToEmpty;
}
private static class ResolutionBundle {
public boolean wasNegated;
public Expression badEqualsCheck;
public Expression collectionToFix;
public ResolutionBundle(Expression collectionToFix, InfixExpression badEqualsCheck, boolean wasNegated) {
this.collectionToFix = collectionToFix;
this.badEqualsCheck = badEqualsCheck;
this.wasNegated = wasNegated;
}
}
private static class IsEmptyVisitor extends ASTVisitor {
public List<ResolutionBundle> resolutionBundles = new ArrayList<>();
@SuppressFBWarnings(value = "PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS",
justification = "The extra local variables would make things more confusing.")
@Override
public boolean visit(InfixExpression node) {
if (node.getOperator() == InfixExpression.Operator.EQUALS ||
node.getOperator() == InfixExpression.Operator.NOT_EQUALS) {
Expression left = node.getLeftOperand();
Expression right = node.getRightOperand();
Object rightConst = right.resolveConstantExpressionValue();
Object leftConst = left.resolveConstantExpressionValue();
if (left instanceof MethodInvocation && rightConst instanceof Integer) {
if (rightConst.equals(0)) {
foundPotentialNewCollection((MethodInvocation) left, node);
}
} else if (right instanceof MethodInvocation && leftConst instanceof Integer) {
if (leftConst.equals(0)) {
foundPotentialNewCollection((MethodInvocation) right, node);
}
}
}
return true;
}
private void foundPotentialNewCollection(MethodInvocation callToSize, InfixExpression node) {
if ("size".equals(callToSize.getName().getIdentifier())) {
resolutionBundles.add(new ResolutionBundle(callToSize.getExpression(), node,
node.getOperator() == InfixExpression.Operator.NOT_EQUALS));
}
}
}
}