1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules.strings;
5
6 import net.sourceforge.pmd.AbstractRule;
7 import net.sourceforge.pmd.ast.ASTAdditiveExpression;
8 import net.sourceforge.pmd.ast.ASTAllocationExpression;
9 import net.sourceforge.pmd.ast.ASTArgumentList;
10 import net.sourceforge.pmd.ast.ASTBlockStatement;
11 import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
12 import net.sourceforge.pmd.ast.ASTLiteral;
13 import net.sourceforge.pmd.ast.ASTName;
14 import net.sourceforge.pmd.ast.ASTStatementExpression;
15 import net.sourceforge.pmd.ast.Node;
16 import net.sourceforge.pmd.ast.SimpleNode;
17 import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
18
19 import java.util.Iterator;
20 import java.util.List;
21
22
23
24
25
26
27
28
29
30
31 public class InefficientStringBuffering extends AbstractRule {
32
33 public Object visit(ASTAdditiveExpression node, Object data) {
34 ASTBlockStatement bs = (ASTBlockStatement) node.getFirstParentOfType(ASTBlockStatement.class);
35 if (bs == null) {
36 return data;
37 }
38
39 int immediateLiterals = 0;
40 List nodes = node.findChildrenOfType(ASTLiteral.class);
41 for (Iterator i = nodes.iterator();i.hasNext();) {
42 ASTLiteral literal = (ASTLiteral)i.next();
43 if (literal.jjtGetParent().jjtGetParent().jjtGetParent() instanceof ASTAdditiveExpression) {
44 immediateLiterals++;
45 }
46 try {
47 Integer.parseInt(literal.getImage());
48 return data;
49 } catch (NumberFormatException nfe) {
50
51 }
52 }
53
54 if (immediateLiterals > 1) {
55 return data;
56 }
57
58
59 List nameNodes = node.findChildrenOfType(ASTName.class);
60 for (Iterator i = nameNodes.iterator(); i.hasNext();) {
61 ASTName name = (ASTName)i.next();
62 if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
63 VariableNameDeclaration vnd = (VariableNameDeclaration)name.getNameDeclaration();
64 if (vnd.getAccessNodeParent().isFinal() && vnd.getAccessNodeParent().isStatic()) {
65 return data;
66 }
67 }
68 }
69
70
71 if (bs.isAllocation()) {
72 if (isAllocatedStringBuffer(node)) {
73 addViolation(data, node);
74 }
75 } else if (isInStringBufferAppend(node, 6)) {
76 addViolation(data, node);
77 }
78 return data;
79 }
80
81 protected static boolean isInStringBufferAppend(SimpleNode node, int length) {
82 if (!xParentIsStatementExpression(node, length)) {
83 return false;
84 }
85 ASTStatementExpression s = (ASTStatementExpression) node.getFirstParentOfType(ASTStatementExpression.class);
86 if (s == null) {
87 return false;
88 }
89 ASTName n = (ASTName)s.getFirstChildOfType(ASTName.class);
90
91 if (n == null || n.getImage().indexOf("append") == -1 || !(n.getNameDeclaration() instanceof VariableNameDeclaration)) {
92 return false;
93 }
94
95
96
97
98
99 ASTArgumentList argList = (ASTArgumentList)s.getFirstChildOfType(ASTArgumentList.class);
100 if (argList == null || argList.jjtGetNumChildren() > 1) {
101 return false;
102 }
103
104 return ((VariableNameDeclaration)n.getNameDeclaration()).getTypeImage().equals("StringBuffer");
105 }
106
107
108 private static boolean xParentIsStatementExpression(SimpleNode node, int length) {
109 Node curr = node;
110 for (int i=0; i<length; i++) {
111 if (node.jjtGetParent() == null) {
112 return false;
113 }
114 curr = curr.jjtGetParent();
115 }
116 return curr instanceof ASTStatementExpression;
117 }
118
119 private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
120 ASTAllocationExpression ao = (ASTAllocationExpression) node.getFirstParentOfType(ASTAllocationExpression.class);
121 if (ao == null) {
122 return false;
123 }
124
125 ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
126 return an != null && (an.getImage().endsWith("StringBuffer") || an.getImage().endsWith("StringBuilder"));
127 }
128 }
129