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 (isInStringBufferOperation(node, 6, "append")) {
76 addViolation(data, node);
77 }
78 return data;
79 }
80
81 protected static boolean isInStringBufferOperation(SimpleNode node, int length, String methodName) {
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 if (n == null || n.getImage().indexOf(methodName) == -1 || !(n.getNameDeclaration() instanceof VariableNameDeclaration)) {
91 return false;
92 }
93
94
95
96
97
98 ASTArgumentList argList = (ASTArgumentList)s.getFirstChildOfType(ASTArgumentList.class);
99 if (argList == null || argList.jjtGetNumChildren() > 1) {
100 return false;
101 }
102
103 return ((VariableNameDeclaration)n.getNameDeclaration()).getTypeImage().equals("StringBuffer");
104 }
105
106
107 private static boolean xParentIsStatementExpression(SimpleNode node, int length) {
108 Node curr = node;
109 for (int i=0; i<length; i++) {
110 if (node.jjtGetParent() == null) {
111 return false;
112 }
113 curr = curr.jjtGetParent();
114 }
115 return curr instanceof ASTStatementExpression;
116 }
117
118 private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
119 ASTAllocationExpression ao = (ASTAllocationExpression) node.getFirstParentOfType(ASTAllocationExpression.class);
120 if (ao == null) {
121 return false;
122 }
123
124 ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
125 return an != null && (an.getImage().endsWith("StringBuffer") || an.getImage().endsWith("StringBuilder"));
126 }
127 }
128