package quickfix;
import static edu.umd.cs.findbugs.plugin.eclipse.quickfix.util.ASTUtil.getASTNode;
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.CustomLabelVisitor;
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.ITypeBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
public class IsNANResolution 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());
IsNANVisitor visitor = new IsNANVisitor();
node.accept(visitor);
Expression fixedExpression = makeFixedExpression(rewrite, visitor);
rewrite.replace(visitor.infixToReplace, fixedExpression, null);
}
private static String doubleOrFloat(boolean isDouble) {
return isDouble ? "Double" : "Float";
}
@SuppressWarnings("unchecked")
private Expression makeFixedExpression(ASTRewrite rewrite, IsNANVisitor visitor) {
AST ast = rewrite.getAST();
MethodInvocation fixedMethod = ast.newMethodInvocation();
fixedMethod.setName(ast.newSimpleName("isNaN"));
if (visitor.isPrimitive) {
// make a reference to Double or Float
SimpleName staticType = ast.newSimpleName(doubleOrFloat(visitor.isDouble));
fixedMethod.setExpression(staticType);
fixedMethod.arguments().add(rewrite.createMoveTarget(visitor.testedVariable));
} else {
// call isNaN directly on the boxed variable
fixedMethod.setExpression((Expression) rewrite.createMoveTarget(visitor.testedVariable));
}
if (!visitor.isEquals) {
PrefixExpression not = ast.newPrefixExpression();
not.setOperator(PrefixExpression.Operator.NOT);
not.setOperand(fixedMethod);
return not;
}
return fixedMethod;
}
private static class IsNANVisitor extends ASTVisitor implements CustomLabelVisitor {
public InfixExpression infixToReplace;
public SimpleName testedVariable;
public boolean isEquals;
public boolean isDouble;
public boolean isPrimitive;
@Override
@SuppressFBWarnings(value = "PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS",
justification = "Fixing the duplications would not impact performance and probably harm readibility")
public boolean visit(InfixExpression node) {
if (infixToReplace != null) {
return false;
}
this.infixToReplace = node;
if (node.getOperator() == Operator.EQUALS) {
this.isEquals = true;
} else if (node.getOperator() == Operator.NOT_EQUALS) {
this.isEquals = false;
}
if (node.getLeftOperand() instanceof SimpleName) {
handleVariable((SimpleName) node.getLeftOperand());
} else if (node.getRightOperand() instanceof SimpleName) {
handleVariable((SimpleName) node.getRightOperand());
}
return true;
}
private void handleVariable(SimpleName side) {
this.testedVariable = side;
ITypeBinding type = this.testedVariable.resolveTypeBinding();
this.isPrimitive = type.isPrimitive();
String name = type.getName();
this.isDouble = "double".equals(name) || "Double".equals(name);
}
@Override
public String getLabelReplacement() {
if (isPrimitive) {
return String.format("a call to %s%s.isNaN(%s)", isEquals ? "" : "!",
doubleOrFloat(isDouble), testedVariable.getIdentifier());
}
return String.format("%s%s.isNaN()", isEquals ? "" : "!", testedVariable.getIdentifier());
}
}
@Override
protected ASTVisitor getCustomLabelVisitor() {
return new IsNANVisitor();
}
}