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 }