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