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       * 
36       * @return map
37       */
38      public Map<String, List<String>> getComparisonTargets() {
39          Map<String, List<String>> rules = new HashMap<>();
40          rules.put("==", Arrays.asList("0"));
41          rules.put("!=", Arrays.asList("0"));
42          rules.put(">", Arrays.asList("0"));
43          rules.put("<", Arrays.asList("0"));
44          return rules;
45      }
46  
47      private static Map<String, String> inverse = new HashMap<>();
48      static {
49          inverse.put("<", ">");
50          inverse.put(">", "<");
51          inverse.put("<=", ">=");
52          inverse.put(">=", "<=");
53          inverse.put("==", "==");
54          inverse.put("!=", "!=");
55      }
56  
57      public Object visit(ASTVariableDeclaratorId node, Object data) {
58          Node nameNode = node.getTypeNameNode();
59          if (nameNode == null || 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
78       * that compares with a size() call.
79       * 
80       * @param data
81       *            the rule context
82       * @param location
83       *            the node location to report
84       * @param expr
85       *            the ==, <, > expression
86       */
87      protected void checkNodeAndReport(Object data, Node location, Node expr) {
88          if ((expr instanceof ASTEqualityExpression || expr instanceof ASTRelationalExpression
89                  && getComparisonTargets().containsKey(expr.getImage()))
90                  && isCompare(expr)) {
91              addViolation(data, location);
92          }
93      }
94  
95      /**
96       * We only need to report if this is comparing against one of the comparison
97       * targets
98       * 
99       * @param equality
100      * @return true if this is comparing to one of the comparison targets else
101      *         false
102      * @see #getComparisonTargets()
103      */
104     private boolean isCompare(Node equality) {
105         if (isLiteralLeftHand(equality)) {
106             return checkComparison(inverse.get(equality.getImage()), equality, 0);
107         } else if (isLiteralRightHand(equality)) {
108             return checkComparison(equality.getImage(), equality, 1);
109         }
110         return false;
111     }
112 
113     private boolean isLiteralLeftHand(Node equality) {
114         return isLiteral(equality, 0);
115     }
116 
117     private boolean isLiteralRightHand(Node equality) {
118         return isLiteral(equality, 1);
119     }
120 
121     private boolean isLiteral(Node equality, int child) {
122         Node target = equality.jjtGetChild(child);
123         target = getFirstChildOrThis(target);
124         target = getFirstChildOrThis(target);
125         return target instanceof ASTLiteral;
126     }
127 
128     private Node getFirstChildOrThis(Node node) {
129         if (node.jjtGetNumChildren() > 0) {
130             return node.jjtGetChild(0);
131         }
132         return node;
133     }
134 
135     /**
136      * Checks if the equality expression passed in is of comparing against the
137      * value passed in as i
138      * 
139      * @param equality
140      * @param i
141      *            The ordinal in the equality expression to check
142      * @return true if the value in position i is one of the comparison targets,
143      *         else false
144      * @see #getComparisonTargets()
145      */
146     private boolean checkComparison(String operator, Node equality, int i) {
147         Node target = equality
148                 .jjtGetChild(i)
149                 .jjtGetChild(0)
150                 .jjtGetChild(0);
151         return target instanceof ASTLiteral && getComparisonTargets().get(operator).contains(target.getImage());
152     }
153 
154 }