View Javadoc

1   package net.sourceforge.pmd.rules;
2   
3   import java.util.Iterator;
4   import java.util.List;
5   import java.util.Set;
6   
7   import net.sourceforge.pmd.AbstractRule;
8   import net.sourceforge.pmd.ast.ASTConditionalExpression;
9   import net.sourceforge.pmd.ast.ASTExpression;
10  import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
11  import net.sourceforge.pmd.ast.ASTPrimaryExpression;
12  import net.sourceforge.pmd.ast.ASTPrimarySuffix;
13  import net.sourceforge.pmd.ast.ASTType;
14  import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
15  import net.sourceforge.pmd.ast.Node;
16  import net.sourceforge.pmd.ast.SimpleNode;
17  import net.sourceforge.pmd.symboltable.NameOccurrence;
18  import net.sourceforge.pmd.util.CollectionUtil;
19  
20  /**
21   * An operation on an Immutable object (BigDecimal or BigInteger) won't change
22   * the object itself. The result of the operation is a new object. Therefore,
23   * ignoring the operation result is an error.
24   */
25  public class UselessOperationOnImmutable extends AbstractRule {
26      /**
27       * These are the methods which are immutable
28       */
29      private static final Set<String> targetMethods = CollectionUtil.asSet(new String[] { ".add", ".multiply", ".divide", ".subtract", ".setScale", ".negate", ".movePointLeft", ".movePointRight", ".pow", ".shiftLeft", ".shiftRight" });
30  
31      /**
32       * These are the classes that the rule can apply to
33       */
34      private static final Set<String> targetClasses = CollectionUtil.asSet(new String[] { "java.math.BigDecimal", "BigDecimal", "java.math.BigInteger", "BigInteger" });
35  
36      public Object visit(ASTLocalVariableDeclaration node, Object data) {
37  
38          ASTVariableDeclaratorId var = getDeclaration(node);
39          if (var == null) {
40              return super.visit(node, data);
41          }
42          String variableName = var.getImage();
43          for (NameOccurrence no: var.getUsages()) {
44              // FIXME - getUsages will return everything with the same name as the variable, 
45              // see JUnit test, case 6. Changing to SimpleNode below, revisit when getUsages is fixed
46              SimpleNode sn = no.getLocation();
47              Node primaryExpression = sn.jjtGetParent().jjtGetParent();
48  			Class<? extends Node> parentClass = primaryExpression.jjtGetParent().getClass();
49              if (!(parentClass.equals(ASTExpression.class) || parentClass.equals(ASTConditionalExpression.class) || 
50              		hasComparisons(primaryExpression))) {
51                  String methodCall = sn.getImage().substring(variableName.length());
52                  if (targetMethods.contains(methodCall)) {
53                      addViolation(data, sn);
54                  }
55              }
56          }
57          return super.visit(node, data);
58      }
59  
60      /**
61       * Check whether the Immutable is compareTo'd something
62       */
63  	private boolean hasComparisons(Node primaryExpression) {
64  		if (primaryExpression.getClass().equals(ASTPrimaryExpression.class)) {
65  			List<ASTPrimarySuffix> suffixes = ((ASTPrimaryExpression)primaryExpression).findChildrenOfType(ASTPrimarySuffix.class);
66  			for (Iterator<ASTPrimarySuffix> iterator = suffixes.iterator(); iterator.hasNext();) {
67  				ASTPrimarySuffix suffix = iterator.next();
68  				if ("compareTo".equals(suffix.getImage()))
69  					return true;
70  			}
71  		} else {
72  			//Some weird usage of the Immutable
73  		}
74  		return false;	//No comparison
75  	}
76  
77      /**
78       * This method checks the variable declaration if it is on a class we care
79       * about. If it is, it returns the DeclaratorId
80       * 
81       * @param node
82       *            The ASTLocalVariableDeclaration which is a problem
83       * @return ASTVariableDeclaratorId
84       */
85      private ASTVariableDeclaratorId getDeclaration(ASTLocalVariableDeclaration node) {
86          ASTType type = node.getTypeNode();
87          if (targetClasses.contains(type.getTypeImage())) {
88              return (ASTVariableDeclaratorId) node.jjtGetChild(1).jjtGetChild(0);
89          }
90          return null;
91      }
92  }