View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule;
5   
6   import java.util.Arrays;
7   import java.util.HashMap;
8   import java.util.List;
9   import java.util.Map;
10  
11  import net.sourceforge.pmd.lang.ast.Node;
12  import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
13  import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
14  import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
15  import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression;
16  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
17  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
18  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
19  
20  /**
21   * This is an abstract rule for patterns which compare a method invocation to 0.
22   * It could be further abstracted to find code that compares something to
23   * another definable pattern
24   * 
25   * @author acaplan
26   */
27  public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule {
28  
29      public abstract boolean appliesToClassName(String name);
30  
31      public abstract boolean isTargetMethod(JavaNameOccurrence occ);
32  
33      /**
34       * For each relation/equality operator, comparison targets need to define.
35       * @return map
36       */
37      public Map<String, List<String>> getComparisonTargets() {
38          Map<String, List<String>> rules = new HashMap<String, List<String>>();
39          rules.put("==", Arrays.asList("0"));
40          rules.put("!=", Arrays.asList("0"));
41          rules.put(">", Arrays.asList("0"));
42          rules.put("<", Arrays.asList("0"));
43          return rules;
44      }
45  
46      private static Map<String, String> inverse = new HashMap<String, String>();
47      static {
48          inverse.put("<", ">");
49          inverse.put(">", "<");
50          inverse.put("<=", ">=");
51          inverse.put(">=", "<=");
52          inverse.put("==", "==");
53          inverse.put("!=", "!=");
54      }
55  
56      public Object visit(ASTVariableDeclaratorId node, Object data) {
57          Node nameNode = node.getTypeNameNode();
58          if (nameNode == null
59              || nameNode instanceof ASTPrimitiveType
60              || !appliesToClassName(node.getNameDeclaration().getTypeImage())) {
61              return data;
62          }
63  
64          List<NameOccurrence> declars = node.getUsages();
65          for (NameOccurrence occ: declars) {
66              JavaNameOccurrence jocc = (JavaNameOccurrence)occ;
67              if (!isTargetMethod(jocc)) {
68                  continue;
69              }
70              Node expr = jocc.getLocation().jjtGetParent().jjtGetParent().jjtGetParent();
71              checkNodeAndReport(data, jocc.getLocation(), expr);
72          }
73          return data;
74      }
75  
76      /**
77       * Checks whether the given expression is a equality/relation expression that
78       * compares with a size() call.
79       * 
80       * @param data the rule context
81       * @param location the node location to report
82       * @param expr the ==, <, > expression
83       */
84      protected void checkNodeAndReport(Object data, Node location, Node expr) {
85          if ((expr instanceof ASTEqualityExpression
86              || (expr instanceof ASTRelationalExpression
87                      && (getComparisonTargets().containsKey(expr.getImage()))))
88              && isCompare(expr)) {
89              addViolation(data, location);
90          }
91      }
92  
93      /**
94       * We only need to report if this is comparing against one of the comparison targets
95       * 
96       * @param equality
97       * @return true if this is comparing to one of the comparison targets else false
98       * @see #getComparisonTargets()
99       */
100     private boolean isCompare(Node equality) {
101         if (isLiteralLeftHand(equality)) {
102             return checkComparison(inverse.get(equality.getImage()), equality, 0);
103         } else {
104             return checkComparison(equality.getImage(), equality, 1);
105         }
106     }
107 
108     private boolean isLiteralLeftHand(Node equality) {
109         return equality.jjtGetChild(0).jjtGetChild(0).jjtGetNumChildren() > 0
110                 && equality.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral;
111     }
112 
113     /**
114      * Checks if the equality expression passed in is of comparing against the
115      * value passed in as i
116      * 
117      * @param equality
118      * @param i
119      *            The ordinal in the equality expression to check
120      * @return true if the value in position i is one of the comparison targets, else false
121      * @see #getComparisonTargets()
122      */
123     private boolean checkComparison(String operator, Node equality, int i) {
124         Node target = equality.jjtGetChild(i).jjtGetChild(0).jjtGetChild(0);
125         return target instanceof ASTLiteral && getComparisonTargets().get(operator).contains(target.getImage());
126     }
127 
128 }