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.List;
8   
9   import net.sourceforge.pmd.lang.ast.Node;
10  import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
11  import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
12  import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
13  import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression;
14  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
15  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
16  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
17  
18  /**
19   * This is an abstract rule for patterns which compare a method invocation to 0.
20   * It could be further abstracted to find code that compares something to
21   * another definable pattern
22   * 
23   * @author acaplan
24   */
25  public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule {
26  
27      public abstract boolean appliesToClassName(String name);
28  
29      public abstract boolean isTargetMethod(JavaNameOccurrence occ);
30  
31      public List<String> getComparisonTargets() {
32          return Arrays.asList("0");
33      }
34  
35      public Object visit(ASTVariableDeclaratorId node, Object data) {
36          Node nameNode = node.getTypeNameNode();
37          if (nameNode == null
38              || nameNode instanceof ASTPrimitiveType
39              || !appliesToClassName(node.getNameDeclaration().getTypeImage())) {
40              return data;
41          }
42  
43          List<NameOccurrence> declars = node.getUsages();
44          for (NameOccurrence occ: declars) {
45              JavaNameOccurrence jocc = (JavaNameOccurrence)occ;
46              if (!isTargetMethod(jocc)) {
47                  continue;
48              }
49              Node expr = jocc.getLocation().jjtGetParent().jjtGetParent().jjtGetParent();
50              checkNodeAndReport(data, jocc.getLocation(), expr);
51          }
52          return data;
53      }
54  
55      /**
56       * Checks whether the given expression is a equality/relation expression that
57       * compares with a size() call.
58       * 
59       * @param data the rule context
60       * @param location the node location to report
61       * @param expr the ==, <, > expression
62       */
63      protected void checkNodeAndReport(Object data, Node location, Node expr) {
64          if ((expr instanceof ASTEqualityExpression
65              || (expr instanceof ASTRelationalExpression
66                      && (">".equals(expr.getImage()) || "<".equals(expr.getImage()))))
67              && isCompare(expr)) {
68              addViolation(data, location);
69          }
70      }
71  
72      /**
73       * We only need to report if this is comparing against one of the comparison targets
74       * 
75       * @param equality
76       * @return true if this is comparing to one of the comparison targets else false
77       * @see #getComparisonTargets()
78       */
79      private boolean isCompare(Node equality) {
80          return checkComparison(equality, 0) || checkComparison(equality, 1);
81  
82      }
83  
84      /**
85       * Checks if the equality expression passed in is of comparing against the
86       * value passed in as i
87       * 
88       * @param equality
89       * @param i
90       *            The ordinal in the equality expression to check
91       * @return true if the value in position i is one of the comparison targets, else false
92       * @see #getComparisonTargets()
93       */
94      private boolean checkComparison(Node equality, int i) {
95  	Node target = equality.jjtGetChild(i).jjtGetChild(0);
96          if (target.jjtGetNumChildren() == 0) {
97              return false;
98          }
99          target = target.jjtGetChild(0);
100         return target instanceof ASTLiteral && getComparisonTargets().indexOf(target.getImage()) > -1;
101     }
102 
103 }