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 import net.sourceforge.pmd.typeresolution.TypeHelper;
19
20 import java.util.Iterator;
21 import java.util.List;
22
23
24
25
26
27
28
29
30
31
32 public class InefficientStringBuffering extends AbstractRule {
33
34 public Object visit(ASTAdditiveExpression node, Object data) {
35 ASTBlockStatement bs = node.getFirstParentOfType(ASTBlockStatement.class);
36 if (bs == null) {
37 return data;
38 }
39
40 int immediateLiterals = 0;
41 List<ASTLiteral> nodes = node.findChildrenOfType(ASTLiteral.class);
42 for (ASTLiteral literal: nodes) {
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<ASTName> nameNodes = node.findChildrenOfType(ASTName.class);
60 for (ASTName name: nameNodes) {
61 if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
62 VariableNameDeclaration vnd = (VariableNameDeclaration)name.getNameDeclaration();
63 if (vnd.getAccessNodeParent().isFinal() && vnd.getAccessNodeParent().isStatic()) {
64 return data;
65 }
66 }
67 }
68
69 if (bs.isAllocation()) {
70 for (Iterator<ASTName> iterator = nameNodes.iterator(); iterator.hasNext();) {
71 ASTName name = iterator.next();
72 if (!name.getImage().endsWith("length")) {
73 break;
74 } else if (!iterator.hasNext()) {
75 return data;
76 }
77 }
78
79 if (isAllocatedStringBuffer(node)) {
80 addViolation(data, node);
81 }
82 } else if (isInStringBufferOperation(node, 6, "append")) {
83 addViolation(data, node);
84 }
85 return data;
86 }
87
88 protected static boolean isInStringBufferOperation(SimpleNode node, int length, String methodName) {
89 if (!xParentIsStatementExpression(node, length)) {
90 return false;
91 }
92 ASTStatementExpression s = node.getFirstParentOfType(ASTStatementExpression.class);
93 if (s == null) {
94 return false;
95 }
96 ASTName n = s.getFirstChildOfType(ASTName.class);
97 if (n == null || n.getImage().indexOf(methodName) == -1 || !(n.getNameDeclaration() instanceof VariableNameDeclaration)) {
98 return false;
99 }
100
101
102
103
104
105 ASTArgumentList argList = s.getFirstChildOfType(ASTArgumentList.class);
106 if (argList == null || argList.jjtGetNumChildren() > 1) {
107 return false;
108 }
109 return TypeHelper.isA((VariableNameDeclaration)n.getNameDeclaration(), StringBuffer.class);
110 }
111
112
113 private static boolean xParentIsStatementExpression(SimpleNode node, int length) {
114 Node curr = node;
115 for (int i=0; i<length; i++) {
116 if (node.jjtGetParent() == null) {
117 return false;
118 }
119 curr = curr.jjtGetParent();
120 }
121 return curr instanceof ASTStatementExpression;
122 }
123
124 private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
125 ASTAllocationExpression ao = node.getFirstParentOfType(ASTAllocationExpression.class);
126 if (ao == null) {
127 return false;
128 }
129
130 ASTClassOrInterfaceType an = ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
131 return an != null && (TypeHelper.isA(an, StringBuffer.class) || TypeHelper.isA(an, StringBuilder.class));
132 }
133 }
134