Clover coverage report - PMD - 3.9 Coverage timestamp: Tue Dec 19 2006 09:38:44 EST FRAMES NO FRAMES |
file stats: | LOC: | 315 |  | Methods: | 11
| NCLOC: | 243 |  | Classes: | 1
Source file | Conditionals | Statements | Methods | TOTAL
InsufficientStringBufferDeclaration.java | 94% | 98% | 100% | 96.7% |

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.ASTBlockStatement;
9 |  | import net.sourceforge.pmd.ast.ASTFieldDeclaration;
10 |  | import net.sourceforge.pmd.ast.ASTFormalParameter;
11 |  | import net.sourceforge.pmd.ast.ASTIfStatement;
12 |  | import net.sourceforge.pmd.ast.ASTLiteral;
13 |  | import net.sourceforge.pmd.ast.ASTName;
14 |  | import net.sourceforge.pmd.ast.ASTPrimaryExpression;
15 |  | import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
16 |  | import net.sourceforge.pmd.ast.ASTPrimarySuffix;
17 |  | import net.sourceforge.pmd.ast.ASTSwitchLabel;
18 |  | import net.sourceforge.pmd.ast.ASTSwitchStatement;
19 |  | import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
20 |  | import net.sourceforge.pmd.ast.Node;
21 |  | import net.sourceforge.pmd.ast.SimpleNode;
22 |  | import net.sourceforge.pmd.symboltable.NameOccurrence;
23 |  |
24 |  | import java.util.HashMap;
25 |  | import java.util.HashSet;
26 |  | import java.util.Iterator;
27 |  | import java.util.List;
28 |  | import java.util.Map;
29 |  | import java.util.Set;
30 |  |
31 |  | /**
32 |  | * This rule finds StringBuffers which may have been pre-sized incorrectly
33 |  | *
34 |  | * See http://sourceforge.net/forum/forum.php?thread_id=1438119&forum_id=188194
35 |  | * @author Allan Caplan
36 |  | */
37 |  | public class InsufficientStringBufferDeclaration extends AbstractRule {
38 |  |
39 |  | private final static Set blockParents;
40 |  |
41 |  | static {
42 | 14 | blockParents = new HashSet();
43 | 14 | blockParents.add(ASTIfStatement.class);
44 | 14 | blockParents.add(ASTSwitchStatement.class);
45 |  | }
46 |  |
47 | 69 | public Object visit(ASTVariableDeclaratorId node, Object data) {
48 |  |
49 | 69 | if (!"StringBuffer".equals(node.getNameDeclaration().getTypeImage())) {
50 | 34 | return data;
51 |  | }
52 | 35 | Node rootNode = node;
53 | 35 | int anticipatedLength = 0;
54 | 35 | int constructorLength = 16;
55 |  |
56 | 35 | constructorLength = getConstructorLength(node, constructorLength);
57 | 35 | anticipatedLength = getInitialLength(node);
58 | 35 | List usage = node.getUsages();
59 | 35 | Map blocks = new HashMap();
60 | 35 | for (int ix = 0; ix < usage.size(); ix++) {
61 | 83 | NameOccurrence no = (NameOccurrence) usage.get(ix);
62 | 83 | SimpleNode n = no.getLocation();
63 | 83 | if (!InefficientStringBuffering.isInStringBufferOperation(n, 3, "append")) {
64 |  |
65 | 7 | if (!no.isOnLeftHandSide() && !InefficientStringBuffering.isInStringBufferOperation(n, 3, "setLength")) {
66 | 2 | continue;
67 |  | }
68 | 5 | if (constructorLength != -1 && anticipatedLength > constructorLength) {
69 | 1 | anticipatedLength += processBlocks(blocks);
70 | 1 | String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
71 | 1 | addViolation(data, rootNode, param);
72 |  | }
73 | 5 | constructorLength = getConstructorLength(n, constructorLength);
74 | 5 | rootNode = n;
75 | 5 | anticipatedLength = getInitialLength(node);
76 |  | }
77 | 81 | ASTPrimaryExpression s = (ASTPrimaryExpression) n.getFirstParentOfType(ASTPrimaryExpression.class);
78 | 81 | int numChildren = s.jjtGetNumChildren();
79 | 81 | for (int jx = 0; jx < numChildren; jx++) {
80 | 163 | SimpleNode sn = (SimpleNode) s.jjtGetChild(jx);
81 | 163 | if (!(sn instanceof ASTPrimarySuffix) || sn.getImage() != null) {
82 | 83 | continue;
83 |  | }
84 | 80 | int thisSize = 0;
85 | 80 | Node block = getFirstParentBlock(sn);
86 | 80 | if (isAdditive(sn)) {
87 | 2 | thisSize = processAdditive(sn);
88 |  | } else {
89 | 78 | thisSize = processNode(sn);
90 |  | }
91 | 80 | if (block != null) {
92 | 31 | storeBlockStatistics(blocks, thisSize, block);
93 |  | } else {
94 | 49 | anticipatedLength += thisSize;
95 |  | }
96 |  | }
97 |  | }
98 | 35 | anticipatedLength += processBlocks(blocks);
99 | 35 | if (constructorLength != -1 && anticipatedLength > constructorLength) {
100 | 13 | String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
101 | 13 | addViolation(data, rootNode, param);
102 |  | }
103 | 35 | return data;
104 |  | }
105 |  |
106 |  | /**
107 |  | * This rule is concerned with IF and Switch blocks. Process the block into
108 |  | * a local Map, from which we can later determine which is the longest block
109 |  | * inside
110 |  | *
111 |  | * @param blocks
112 |  | * The map of blocks in the method being investigated
113 |  | * @param thisSize
114 |  | * The size of the current block
115 |  | * @param block
116 |  | * The block in question
117 |  | */
118 | 31 | private void storeBlockStatistics(Map blocks, int thisSize, Node block) {
119 | 31 | Node statement = block.jjtGetParent();
120 | 31 | if (ASTIfStatement.class.equals(block.jjtGetParent().getClass())) {
121 |  | // Else Ifs are their own subnode in AST. So we have to
122 |  | // look a little farther up the tree to find the IF statement
123 | 26 | Node possibleStatement = ((SimpleNode) statement).getFirstParentOfType(ASTIfStatement.class);
124 | 26 | while(possibleStatement != null && possibleStatement.getClass().equals(ASTIfStatement.class)) {
125 | 23 | statement = possibleStatement;
126 | 23 | possibleStatement = ((SimpleNode) possibleStatement).getFirstParentOfType(ASTIfStatement.class);
127 |  | }
128 |  | }
129 | 31 | Map thisBranch = (Map) blocks.get(statement);
130 | 31 | if (thisBranch == null) {
131 | 10 | thisBranch = new HashMap();
132 | 10 | blocks.put(statement, thisBranch);
133 |  | }
134 | 31 | Integer x = (Integer) thisBranch.get(block);
135 | 31 | if (x != null) {
136 | 0 | thisSize += x.intValue();
137 |  | }
138 | 31 | thisBranch.put(statement, new Integer(thisSize));
139 |  | }
140 |  |
141 | 36 | private int processBlocks(Map blocks) {
142 | 36 | int anticipatedLength = 0;
143 | 36 | int ifLength = 0;
144 | 36 | for (Iterator iter = blocks.entrySet().iterator(); iter.hasNext();) {
145 | 10 | Map.Entry entry = (Map.Entry) iter.next();
146 | 10 | ifLength = 0;
147 | 10 | for (Iterator iter2 = ((Map) entry.getValue()).entrySet().iterator(); iter2.hasNext();) {
148 | 10 | Map.Entry entry2 = (Map.Entry) iter2.next();
149 | 10 | Integer value = (Integer) entry2.getValue();
150 | 10 | ifLength = Math.max(ifLength, value.intValue());
151 |  | }
152 | 10 | anticipatedLength += ifLength;
153 |  | }
154 | 36 | return anticipatedLength;
155 |  | }
156 |  |
157 | 2 | private int processAdditive(SimpleNode sn) {
158 | 2 | ASTAdditiveExpression additive = (ASTAdditiveExpression) sn.getFirstChildOfType(ASTAdditiveExpression.class);
159 | 2 | if (additive == null) {
160 | 0 | return 0;
161 |  | }
162 | 2 | int anticipatedLength = 0;
163 | 2 | for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) {
164 | 6 | SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix);
165 | 6 | ASTLiteral literal = (ASTLiteral) childNode.getFirstChildOfType(ASTLiteral.class);
166 | 6 | if (literal != null && literal.getImage() != null) {
167 | 5 | anticipatedLength += literal.getImage().length() - 2;
168 |  | }
169 |  | }
170 |  |
171 | 2 | return anticipatedLength;
172 |  | }
173 |  |
174 | 105 | private static final boolean isLiteral(String str) {
175 | 105 | if (str.length() == 0) {
176 | 0 | return false;
177 |  | }
178 | 105 | char c = str.charAt(0);
179 | 105 | return (c == '"' || c == '\'');
180 |  | }
181 |  |
182 | 78 | private int processNode(SimpleNode sn) {
183 | 78 | int anticipatedLength = 0;
184 | 78 | ASTPrimaryPrefix xn = (ASTPrimaryPrefix) sn.getFirstChildOfType(ASTPrimaryPrefix.class);
185 | 78 | if (xn.jjtGetNumChildren() != 0 && xn.jjtGetChild(0).getClass().equals(ASTLiteral.class)) {
186 | 74 | String str = ((SimpleNode) xn.jjtGetChild(0)).getImage();
187 | 74 | if(isLiteral(str)){
188 | 65 | anticipatedLength += str.length() - 2;
189 | 9 | } else if(str.startsWith("0x")){
190 | 5 | anticipatedLength += 1;
191 |  | } else {
192 | 4 | anticipatedLength += str.length();
193 |  | }
194 |  | }
195 | 78 | return anticipatedLength;
196 |  | }
197 |  |
198 | 40 | private int getConstructorLength(SimpleNode node, int constructorLength) {
199 | 40 | int iConstructorLength = constructorLength;
200 | 40 | SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class);
201 | 40 | List literal;
202 |  |
203 | 40 | if (block == null) {
204 | 3 | block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class);
205 |  | }
206 | 40 | if (block == null) {
207 | 1 | block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class);
208 | 1 | if (block != null) {
209 | 1 | iConstructorLength = -1;
210 |  | }
211 |  | }
212 | 40 | literal = (block.findChildrenOfType(ASTLiteral.class));
213 | 40 | if (literal.isEmpty()) {
214 | 20 | List name = (block.findChildrenOfType(ASTName.class));
215 | 20 | if (!name.isEmpty()) {
216 | 2 | iConstructorLength = -1;
217 |  | }
218 | 20 | } else if (literal.size() == 1) {
219 | 18 | String str = ((SimpleNode) literal.get(0)).getImage();
220 | 18 | if (str == null) {
221 | 1 | iConstructorLength = 0;
222 | 17 | } else if (isLiteral(str)) {
223 |  | // since it's not taken into account
224 |  | // anywhere. only count the extra 16
225 |  | // characters
226 | 1 | iConstructorLength = 14 + str.length(); // don't add the constructor's length,
227 |  | } else {
228 | 16 | iConstructorLength = Integer.parseInt(str);
229 |  | }
230 |  | } else {
231 | 2 | iConstructorLength = -1;
232 |  | }
233 |  |
234 | 40 | if(iConstructorLength == 0){
235 | 3 | iConstructorLength = 16;
236 |  | }
237 |  |
238 | 40 | return iConstructorLength;
239 |  | }
240 |  |
241 |  |
242 | 40 | private int getInitialLength(SimpleNode node) {
243 | 40 | SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class);
244 | 40 | List literal;
245 |  |
246 | 40 | if (block == null) {
247 | 3 | block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class);
248 | 3 | if (block == null) {
249 | 1 | block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class);
250 |  | }
251 |  | }
252 | 40 | literal = (block.findChildrenOfType(ASTLiteral.class));
253 | 40 | if (literal.size() == 1) {
254 | 16 | String str = ((SimpleNode) literal.get(0)).getImage();
255 | 16 | if (str != null && isLiteral(str)) {
256 | 1 | return str.length() - 2; // take off the quotes
257 |  | }
258 |  | }
259 |  |
260 | 39 | return 0;
261 |  | }
262 |  |
263 | 80 | private boolean isAdditive(SimpleNode n) {
264 | 80 | return n.findChildrenOfType(ASTAdditiveExpression.class).size() >= 1;
265 |  | }
266 |  |
267 |  | /**
268 |  | * Locate the block that the given node is in, if any
269 |  | *
270 |  | * @param node
271 |  | * The node we're looking for a parent of
272 |  | * @return Node - The node that corresponds to any block that may be a
273 |  | * parent of this object
274 |  | */
275 | 80 | private Node getFirstParentBlock(Node node) {
276 | 80 | Node parentNode = node.jjtGetParent();
277 |  |
278 | 80 | Node lastNode = node;
279 | 80 | while (parentNode != null && !blockParents.contains(parentNode.getClass())) {
280 | 715 | lastNode = parentNode;
281 | 715 | parentNode = parentNode.jjtGetParent();
282 |  | }
283 | 80 | if (parentNode != null && ASTIfStatement.class.equals(parentNode.getClass())) {
284 | 26 | parentNode = lastNode;
285 | 54 | } else if (parentNode != null && parentNode.getClass().equals(ASTSwitchStatement.class)) {
286 | 5 | parentNode = getSwitchParent(parentNode, lastNode);
287 |  | }
288 | 80 | return parentNode;
289 |  | }
290 |  |
291 |  | /**
292 |  | * Determine which SwitchLabel we belong to inside a switch
293 |  | *
294 |  | * @param parentNode
295 |  | * The parent node we're looking at
296 |  | * @param lastNode
297 |  | * The last node processed
298 |  | * @return The parent node for the switch statement
299 |  | */
300 | 5 | private static Node getSwitchParent(Node parentNode, Node lastNode) {
301 | 5 | int allChildren = parentNode.jjtGetNumChildren();
302 | 5 | ASTSwitchLabel label = null;
303 | 27 | for (int ix = 0; ix < allChildren; ix++) {
304 | 27 | Node n = parentNode.jjtGetChild(ix);
305 | 27 | if (n.getClass().equals(ASTSwitchLabel.class)) {
306 | 9 | label = (ASTSwitchLabel) n;
307 | 18 | } else if (n.equals(lastNode)) {
308 | 5 | parentNode = label;
309 | 5 | break;
310 |  | }
311 |  | }
312 | 5 | return parentNode;
313 |  | }
314 |  |
315 |  | }