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