1
2
3
4 package net.sourceforge.pmd.lang.java.rule.strings;
5
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import net.sourceforge.pmd.lang.ast.Node;
13 import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
14 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
15 import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
16 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
17 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
18 import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
19 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
20 import net.sourceforge.pmd.lang.java.ast.ASTMultiplicativeExpression;
21 import net.sourceforge.pmd.lang.java.ast.ASTName;
22 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
23 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
24 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
25 import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel;
26 import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
27 import net.sourceforge.pmd.lang.java.ast.ASTType;
28 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
29 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
30 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
31 import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
32
33
34
35
36
37
38
39 public class InsufficientStringBufferDeclarationRule extends AbstractJavaRule {
40
41 private final static Set<Class<? extends Node>> BLOCK_PARENTS;
42
43 static {
44 BLOCK_PARENTS = new HashSet<Class<? extends Node>>(2);
45 BLOCK_PARENTS.add(ASTIfStatement.class);
46 BLOCK_PARENTS.add(ASTSwitchStatement.class);
47 }
48
49 public static final int DEFAULT_BUFFER_SIZE = 16;
50
51 @Override
52 public Object visit(ASTVariableDeclaratorId node, Object data) {
53 if (!TypeHelper.isEither(node.getNameDeclaration(), StringBuffer.class, StringBuilder.class)) {
54 return data;
55 }
56 Node rootNode = node;
57 int anticipatedLength = 0;
58 int constructorLength = DEFAULT_BUFFER_SIZE;
59
60 constructorLength = getConstructorLength(node, constructorLength);
61 anticipatedLength = getInitialLength(node);
62 List<NameOccurrence> usage = node.getUsages();
63 Map<Node, Map<Node, Integer>> blocks = new HashMap<Node, Map<Node, Integer>>();
64 for (int ix = 0; ix < usage.size(); ix++) {
65 NameOccurrence no = usage.get(ix);
66 Node n = no.getLocation();
67 if (!InefficientStringBufferingRule.isInStringBufferOperation(n, 3, "append")) {
68
69 if (!no.isOnLeftHandSide() && !InefficientStringBufferingRule.isInStringBufferOperation(n, 3, "setLength")) {
70 continue;
71 }
72 if (constructorLength != -1 && anticipatedLength > constructorLength) {
73 anticipatedLength += processBlocks(blocks);
74 String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
75 addViolation(data, rootNode, param);
76 }
77 constructorLength = getConstructorLength(n, constructorLength);
78 rootNode = n;
79 anticipatedLength = getInitialLength(node);
80 }
81 ASTPrimaryExpression s = n.getFirstParentOfType(ASTPrimaryExpression.class);
82 int numChildren = s.jjtGetNumChildren();
83 for (int jx = 0; jx < numChildren; jx++) {
84 Node sn = s.jjtGetChild(jx);
85 if (!(sn instanceof ASTPrimarySuffix) || sn.getImage() != null) {
86 continue;
87 }
88 int thisSize = 0;
89 Node block = getFirstParentBlock(sn);
90 if (isAdditive(sn)) {
91 thisSize = processAdditive(sn);
92 } else {
93 thisSize = processNode(sn);
94 }
95 if (block != null) {
96 storeBlockStatistics(blocks, thisSize, block);
97 } else {
98 anticipatedLength += thisSize;
99 }
100 }
101 }
102 anticipatedLength += processBlocks(blocks);
103 if (constructorLength != -1 && anticipatedLength > constructorLength) {
104 String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
105 addViolation(data, rootNode, param);
106 }
107 return data;
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121
122 private void storeBlockStatistics(Map<Node, Map<Node, Integer>> blocks, int thisSize, Node block) {
123 Node statement = block.jjtGetParent();
124 if (block.jjtGetParent() instanceof ASTIfStatement) {
125
126
127 Node possibleStatement = statement.getFirstParentOfType(ASTIfStatement.class);
128 while (possibleStatement instanceof ASTIfStatement) {
129 statement = possibleStatement;
130 possibleStatement = possibleStatement.getFirstParentOfType(ASTIfStatement.class);
131 }
132 }
133 Map<Node, Integer> thisBranch = blocks.get(statement);
134 if (thisBranch == null) {
135 thisBranch = new HashMap<Node, Integer>();
136 blocks.put(statement, thisBranch);
137 }
138 Integer x = thisBranch.get(block);
139 if (x != null) {
140 thisSize += x;
141 }
142 thisBranch.put(statement, thisSize);
143 }
144
145 private int processBlocks(Map<Node, Map<Node, Integer>> blocks) {
146 int anticipatedLength = 0;
147 int ifLength = 0;
148 for (Map.Entry<Node, Map<Node, Integer>> entry: blocks.entrySet()) {
149 ifLength = 0;
150 for (Map.Entry<Node, Integer> entry2: entry.getValue().entrySet()) {
151 Integer value = entry2.getValue();
152 ifLength = Math.max(ifLength, value.intValue());
153 }
154 anticipatedLength += ifLength;
155 }
156 return anticipatedLength;
157 }
158
159 private int processAdditive(Node sn) {
160 ASTAdditiveExpression additive = sn.getFirstDescendantOfType(ASTAdditiveExpression.class);
161 if (additive == null) {
162 return 0;
163 }
164 int anticipatedLength = 0;
165 for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) {
166 Node childNode = additive.jjtGetChild(ix);
167 ASTLiteral literal = childNode.getFirstDescendantOfType(ASTLiteral.class);
168 if (literal != null && literal.getImage() != null) {
169 anticipatedLength += literal.getImage().length() - 2;
170 }
171 }
172
173 return anticipatedLength;
174 }
175
176 private static final boolean isStringOrCharLiteral(ASTLiteral literal) {
177 return literal.isStringLiteral() || literal.isCharLiteral();
178 }
179
180 private int processNode(Node sn) {
181 int anticipatedLength = 0;
182 if ( sn != null ) {
183 ASTPrimaryPrefix xn = sn.getFirstDescendantOfType(ASTPrimaryPrefix.class);
184 if ( xn != null ) {
185 if (xn.jjtGetNumChildren() != 0 && xn.jjtGetChild(0) instanceof ASTLiteral) {
186 ASTLiteral literal = (ASTLiteral) xn.jjtGetChild(0);
187 String str = xn.jjtGetChild(0).getImage();
188 if (str != null) {
189 if(isStringOrCharLiteral(literal)){
190 anticipatedLength += str.length() - 2;
191 } else if(literal.isIntLiteral() && str.startsWith("0x")){
192
193 Node parentNode = literal.jjtGetParent().jjtGetParent().jjtGetParent();
194 if (parentNode instanceof ASTCastExpression
195 && parentNode.getFirstChildOfType(ASTType.class).getType() == char.class) {
196 anticipatedLength += 1;
197 } else {
198
199 anticipatedLength += String.valueOf(Long.parseLong(str.substring(2), 16)).length();
200 }
201 } else {
202 anticipatedLength += str.length();
203 }
204 }
205 }
206 }
207 }
208 StringBuffer sb = new StringBuffer();
209 sb.append(3);
210 return anticipatedLength;
211 }
212
213 private int getConstructorLength(Node node, int constructorLength) {
214 int iConstructorLength = constructorLength;
215 Node block = node.getFirstParentOfType(ASTBlockStatement.class);
216
217 if (block == null) {
218 block = node.getFirstParentOfType(ASTFieldDeclaration.class);
219 }
220 if (block == null) {
221 block = node.getFirstParentOfType(ASTFormalParameter.class);
222 if (block != null) {
223 iConstructorLength = -1;
224 } else {
225 return DEFAULT_BUFFER_SIZE;
226 }
227 }
228
229
230 ASTAdditiveExpression exp = block.getFirstDescendantOfType(ASTAdditiveExpression.class);
231 if (exp != null){
232 return DEFAULT_BUFFER_SIZE;
233 }
234 ASTMultiplicativeExpression mult = block.getFirstDescendantOfType(ASTMultiplicativeExpression.class);
235 if (mult != null){
236 return DEFAULT_BUFFER_SIZE;
237 }
238
239 List<ASTLiteral> literals = block.findDescendantsOfType(ASTLiteral.class);
240 if (literals.isEmpty()) {
241 List<ASTName> name = block.findDescendantsOfType(ASTName.class);
242 if (!name.isEmpty()) {
243 iConstructorLength = -1;
244 }
245 } else if (literals.size() == 1) {
246 ASTLiteral literal = literals.get(0);
247 String str = literal.getImage();
248 if (str == null) {
249 iConstructorLength = 0;
250 } else if (isStringOrCharLiteral(literal)) {
251
252
253
254 iConstructorLength = 14 + str.length();
255 } else if (literal.isIntLiteral() && str.startsWith("0x")) {
256
257 iConstructorLength = Integer.parseInt(str.substring(2), 16);
258 } else {
259 iConstructorLength = Integer.parseInt(str);
260 }
261 } else {
262 iConstructorLength = -1;
263 }
264
265 if (iConstructorLength == 0) {
266 if (constructorLength == -1) {
267 iConstructorLength = DEFAULT_BUFFER_SIZE;
268 } else {
269 iConstructorLength = constructorLength;
270 }
271 }
272
273 return iConstructorLength;
274 }
275
276
277 private int getInitialLength(Node node) {
278
279 Node block = node.getFirstParentOfType(ASTBlockStatement.class);
280
281 if (block == null) {
282 block = node.getFirstParentOfType(ASTFieldDeclaration.class);
283 if (block == null) {
284 block = node.getFirstParentOfType(ASTFormalParameter.class);
285 }
286 }
287 List<ASTLiteral> literals = block.findDescendantsOfType(ASTLiteral.class);
288 if (literals.size() == 1) {
289 ASTLiteral literal = literals.get(0);
290 String str = literal.getImage();
291 if (str != null && isStringOrCharLiteral(literal)) {
292 return str.length() - 2;
293 }
294 }
295
296 return 0;
297 }
298
299 private boolean isAdditive(Node n) {
300 return n.hasDescendantOfType(ASTAdditiveExpression.class);
301 }
302
303
304
305
306
307
308
309
310
311 private Node getFirstParentBlock(Node node) {
312 Node parentNode = node.jjtGetParent();
313
314 Node lastNode = node;
315 while (parentNode != null && !BLOCK_PARENTS.contains(parentNode.getClass())) {
316 lastNode = parentNode;
317 parentNode = parentNode.jjtGetParent();
318 }
319 if (parentNode instanceof ASTIfStatement) {
320 parentNode = lastNode;
321 } else if (parentNode instanceof ASTSwitchStatement) {
322 parentNode = getSwitchParent(parentNode, lastNode);
323 }
324 return parentNode;
325 }
326
327
328
329
330
331
332
333
334
335
336 private static Node getSwitchParent(Node parentNode, Node lastNode) {
337 int allChildren = parentNode.jjtGetNumChildren();
338 ASTSwitchLabel label = null;
339 for (int ix = 0; ix < allChildren; ix++) {
340 Node n = parentNode.jjtGetChild(ix);
341 if (n instanceof ASTSwitchLabel) {
342 label = (ASTSwitchLabel) n;
343 } else if (n.equals(lastNode)) {
344 parentNode = label;
345 break;
346 }
347 }
348 return parentNode;
349 }
350
351 }