Clover coverage report - PMD - 3.7 Coverage timestamp: Wed May 31 2006 09:25:59 EDT FRAMES NO FRAMES |
file stats: | LOC: | 292 |  | Methods: | 9
| NCLOC: | 217 |  | Classes: | 1
Source file | Conditionals | Statements | Methods | TOTAL
InsufficientStringBufferDeclaration.java | 93.1% | 97.7% | 100% | 96.2% |

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