View Javadoc

1   package net.sourceforge.pmd.rules.strings;
2   
3   import net.sourceforge.pmd.AbstractRule;
4   import net.sourceforge.pmd.ast.ASTEqualityExpression;
5   import net.sourceforge.pmd.ast.ASTLiteral;
6   import net.sourceforge.pmd.ast.ASTPrimitiveType;
7   import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
8   import net.sourceforge.pmd.ast.Node;
9   import net.sourceforge.pmd.ast.SimpleNode;
10  import net.sourceforge.pmd.symboltable.NameOccurrence;
11  
12  import java.util.Iterator;
13  import java.util.List;
14  
15  /***
16   * This rule finds code which inefficiently determines empty strings. This code
17   * <p/>
18   * <pre>
19   *     if(str.trim().length()==0){....
20   * </pre>
21   * <p/>
22   * is quite inefficient as trim() causes a new String to be created. Smarter
23   * code to check for an empty string would be:
24   * <p/>
25   * <pre>
26   * Character.isWhitespace(str.charAt(i));
27   * </pre>
28   *
29   * @author acaplan
30   */
31  public class InefficientEmptyStringCheck extends AbstractRule {
32  
33      public Object visit(ASTVariableDeclaratorId node, Object data) {
34          SimpleNode nameNode = node.getTypeNameNode();
35          if (nameNode instanceof ASTPrimitiveType) {
36              return data;
37          }
38  
39          if (!"String".equals(node.getNameDeclaration().getTypeImage())) {
40              return data;
41          }
42  
43          List declars = node.getUsages();
44          for (Iterator i = declars.iterator(); i.hasNext();) {
45              NameOccurrence occ = (NameOccurrence) i.next();
46              if (!isStringLength(occ)) {
47                  continue;
48              }
49              ASTEqualityExpression equality = (ASTEqualityExpression) occ
50                      .getLocation().getFirstParentOfType(ASTEqualityExpression.class);
51              if (equality != null && isCompareZero(equality)) {
52                  addViolation(data, occ.getLocation());
53              }
54          }
55          return data;
56      }
57  
58      /***
59       * We only need to report if this is comparing against 0
60       *
61       * @param equality
62       * @return true if this is comparing to 0 else false
63       */
64      private boolean isCompareZero(ASTEqualityExpression equality) {
65          return (checkComparison(equality, 0) || checkComparison(equality, 1));
66  
67      }
68  
69      /***
70       * Determine if we're dealing with String.length method
71       *
72       * @param occ The name occurance
73       * @return true if it's String.length, else false
74       */
75      private boolean isStringLength(NameOccurrence occ) {
76          if (occ.getNameForWhichThisIsAQualifier() != null
77                  && occ.getNameForWhichThisIsAQualifier().getImage().indexOf("trim") != -1) {
78              Node pExpression = occ.getLocation().jjtGetParent().jjtGetParent();
79              if (pExpression.jjtGetNumChildren() >= 3
80                      && "length"
81                      .equals(((SimpleNode) pExpression.jjtGetChild(2))
82                      .getImage())) {
83                  return true;
84              }
85          }
86          return false;
87      }
88  
89      /***
90       * Checks if the equality expression passed in is of comparing against the
91       * value passed in as i
92       *
93       * @param equality
94       * @param i        The ordinal in the equality expression to check
95       * @return true if the value in position i is 0, else false
96       */
97      private boolean checkComparison(ASTEqualityExpression equality, int i) {
98          return (equality.jjtGetChild(i).jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral && "0"
99                  .equals(((SimpleNode) equality.jjtGetChild(i).jjtGetChild(0)
100                 .jjtGetChild(0)).getImage()));
101     }
102 
103 }