1
2
3
4 package net.sourceforge.pmd.lang.java.rule.strings;
5
6 import java.util.Iterator;
7 import java.util.List;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
11 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
13 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
14 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
15 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
16 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
17 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
18 import net.sourceforge.pmd.lang.java.ast.ASTName;
19 import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
20 import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
21 import net.sourceforge.pmd.lang.java.ast.ASTType;
22 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
23 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
24 import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
25
26
27
28
29
30
31
32
33
34
35 public class InefficientStringBufferingRule extends AbstractJavaRule {
36
37 @Override
38 public Object visit(ASTAdditiveExpression node, Object data) {
39 ASTBlockStatement bs = node.getFirstParentOfType(ASTBlockStatement.class);
40 if (bs == null) {
41 return data;
42 }
43
44 int immediateLiterals = 0;
45 int immediateStringLiterals = 0;
46 List<ASTLiteral> nodes = node.findDescendantsOfType(ASTLiteral.class);
47 for (ASTLiteral literal: nodes) {
48 if (literal.getNthParent(3) instanceof ASTAdditiveExpression) {
49 immediateLiterals++;
50 if (literal.isStringLiteral()) {
51 immediateStringLiterals++;
52 }
53 }
54 if (literal.isIntLiteral() || literal.isFloatLiteral()) {
55 return data;
56 }
57 }
58
59 if (immediateLiterals > 1) {
60 return data;
61 }
62
63
64 List<ASTName> nameNodes = node.findDescendantsOfType(ASTName.class);
65 for (ASTName name: nameNodes) {
66 if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
67 VariableNameDeclaration vnd = (VariableNameDeclaration)name.getNameDeclaration();
68 if (vnd.getAccessNodeParent().isFinal() && vnd.getAccessNodeParent().isStatic()) {
69 return data;
70 }
71 }
72 }
73
74
75 boolean stringFound = false;
76 for (ASTName name: nameNodes) {
77 if (!isPrimitiveType(name) && isStringType(name)) {
78 stringFound = true;
79 break;
80 }
81 }
82 if (!stringFound && immediateStringLiterals == 0) {
83 return data;
84 }
85
86 if (bs.isAllocation()) {
87 for (Iterator<ASTName> iterator = nameNodes.iterator(); iterator.hasNext();) {
88 ASTName name = iterator.next();
89 if (!name.getImage().endsWith("length")) {
90 break;
91 } else if (!iterator.hasNext()) {
92 return data;
93 }
94 }
95
96 if (isAllocatedStringBuffer(node)) {
97 addViolation(data, node);
98 }
99 } else if (isInStringBufferOperation(node, 6, "append")) {
100 addViolation(data, node);
101 }
102 return data;
103 }
104
105 private boolean isStringType(ASTName name) {
106 ASTType type = getTypeNode(name);
107 if (type != null) {
108 List<ASTClassOrInterfaceType> types = type.findDescendantsOfType(ASTClassOrInterfaceType.class);
109 if (!types.isEmpty()) {
110 ASTClassOrInterfaceType typeDeclaration = types.get(0);
111 if (String.class == typeDeclaration.getType() || "String".equals(typeDeclaration.getImage())) {
112 return true;
113 }
114 }
115 }
116 return false;
117 }
118
119 private boolean isPrimitiveType(ASTName name) {
120 ASTType type = getTypeNode(name);
121 if (type != null && !type.findChildrenOfType(ASTPrimitiveType.class).isEmpty()) {
122 return true;
123 }
124 return false;
125 }
126
127 private ASTType getTypeNode(ASTName name) {
128 if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
129 VariableNameDeclaration vnd = (VariableNameDeclaration) name.getNameDeclaration();
130 if (vnd.getAccessNodeParent() instanceof ASTLocalVariableDeclaration) {
131 ASTLocalVariableDeclaration l = (ASTLocalVariableDeclaration)vnd.getAccessNodeParent();
132 return l.getTypeNode();
133 } else if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
134 ASTFormalParameter p = (ASTFormalParameter) vnd.getAccessNodeParent();
135 return p.getTypeNode();
136 }
137 }
138 return null;
139 }
140
141 protected static boolean isInStringBufferOperation(Node node, int length, String methodName) {
142 if (!(node.getNthParent(length) instanceof ASTStatementExpression)) {
143 return false;
144 }
145 ASTStatementExpression s = node.getFirstParentOfType(ASTStatementExpression.class);
146 if (s == null) {
147 return false;
148 }
149 ASTName n = s.getFirstDescendantOfType(ASTName.class);
150 if (n == null || n.getImage().indexOf(methodName) == -1 || !(n.getNameDeclaration() instanceof VariableNameDeclaration)) {
151 return false;
152 }
153
154
155
156
157
158 ASTArgumentList argList = s.getFirstDescendantOfType(ASTArgumentList.class);
159 if (argList == null || argList.jjtGetNumChildren() > 1) {
160 return false;
161 }
162 return TypeHelper.isEither((VariableNameDeclaration)n.getNameDeclaration(), StringBuffer.class, StringBuilder.class);
163 }
164
165 private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
166 ASTAllocationExpression ao = node.getFirstParentOfType(ASTAllocationExpression.class);
167 if (ao == null) {
168 return false;
169 }
170
171 ASTClassOrInterfaceType an = ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
172 return an != null && TypeHelper.isEither(an, StringBuffer.class, StringBuilder.class);
173 }
174 }
175