KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > pmd > rules > strings > InsufficientStringBufferDeclaration


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 JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
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 JavaDoc blockParents;
40
41     static {
42         blockParents = new HashSet JavaDoc();
43         blockParents.add(ASTIfStatement.class);
44         blockParents.add(ASTSwitchStatement.class);
45     }
46
47     public Object JavaDoc visit(ASTVariableDeclaratorId node, Object JavaDoc data) {
48
49         if (!"StringBuffer".equals(node.getNameDeclaration().getTypeImage())) {
50             return data;
51         }
52         Node rootNode = node;
53         int anticipatedLength = 0;
54         int constructorLength = 16;
55
56         constructorLength = getConstructorLength(node, constructorLength);
57         anticipatedLength = getInitialLength(node);
58         List JavaDoc usage = node.getUsages();
59         Map JavaDoc blocks = new HashMap JavaDoc();
60         for (int ix = 0; ix < usage.size(); ix++) {
61             NameOccurrence no = (NameOccurrence) usage.get(ix);
62             SimpleNode n = no.getLocation();
63             if (!InefficientStringBuffering.isInStringBufferOperation(n, 3, "append")) {
64
65                 if (!no.isOnLeftHandSide() && !InefficientStringBuffering.isInStringBufferOperation(n, 3, "setLength")) {
66                     continue;
67                 }
68                 if (constructorLength != -1 && anticipatedLength > constructorLength) {
69                     anticipatedLength += processBlocks(blocks);
70                     String JavaDoc[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
71                     addViolation(data, rootNode, param);
72                 }
73                 constructorLength = getConstructorLength(n, constructorLength);
74                 rootNode = n;
75                 anticipatedLength = getInitialLength(node);
76             }
77             ASTPrimaryExpression s = (ASTPrimaryExpression) n.getFirstParentOfType(ASTPrimaryExpression.class);
78             int numChildren = s.jjtGetNumChildren();
79             for (int jx = 0; jx < numChildren; jx++) {
80                 SimpleNode sn = (SimpleNode) s.jjtGetChild(jx);
81                 if (!(sn instanceof ASTPrimarySuffix) || sn.getImage() != null) {
82                     continue;
83                 }
84                 int thisSize = 0;
85                 Node block = getFirstParentBlock(sn);
86                 if (isAdditive(sn)) {
87                     thisSize = processAdditive(sn);
88                 } else {
89                     thisSize = processNode(sn);
90                 }
91                 if (block != null) {
92                     storeBlockStatistics(blocks, thisSize, block);
93                 } else {
94                     anticipatedLength += thisSize;
95                 }
96             }
97         }
98         anticipatedLength += processBlocks(blocks);
99         if (constructorLength != -1 && anticipatedLength > constructorLength) {
100             String JavaDoc[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
101             addViolation(data, rootNode, param);
102         }
103         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     private void storeBlockStatistics(Map JavaDoc blocks, int thisSize, Node block) {
119         Node statement = block.jjtGetParent();
120         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
Node possibleStatement = ((SimpleNode) statement).getFirstParentOfType(ASTIfStatement.class);
124             while(possibleStatement != null && possibleStatement.getClass().equals(ASTIfStatement.class)) {
125                 statement = possibleStatement;
126                 possibleStatement = ((SimpleNode) possibleStatement).getFirstParentOfType(ASTIfStatement.class);
127             }
128         }
129         Map JavaDoc thisBranch = (Map JavaDoc) blocks.get(statement);
130         if (thisBranch == null) {
131             thisBranch = new HashMap JavaDoc();
132             blocks.put(statement, thisBranch);
133         }
134         Integer JavaDoc x = (Integer JavaDoc) thisBranch.get(block);
135         if (x != null) {
136             thisSize += x.intValue();
137         }
138         thisBranch.put(statement, new Integer JavaDoc(thisSize));
139     }
140
141     private int processBlocks(Map JavaDoc blocks) {
142         int anticipatedLength = 0;
143         int ifLength = 0;
144         for (Iterator JavaDoc iter = blocks.entrySet().iterator(); iter.hasNext();) {
145             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
146             ifLength = 0;
147             for (Iterator JavaDoc iter2 = ((Map JavaDoc) entry.getValue()).entrySet().iterator(); iter2.hasNext();) {
148                 Map.Entry JavaDoc entry2 = (Map.Entry JavaDoc) iter2.next();
149                 Integer JavaDoc value = (Integer JavaDoc) entry2.getValue();
150                 ifLength = Math.max(ifLength, value.intValue());
151             }
152             anticipatedLength += ifLength;
153         }
154         return anticipatedLength;
155     }
156
157     private int processAdditive(SimpleNode sn) {
158         ASTAdditiveExpression additive = (ASTAdditiveExpression) sn.getFirstChildOfType(ASTAdditiveExpression.class);
159         if (additive == null) {
160             return 0;
161         }
162         int anticipatedLength = 0;
163         for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) {
164             SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix);
165             ASTLiteral literal = (ASTLiteral) childNode.getFirstChildOfType(ASTLiteral.class);
166             if (literal != null && literal.getImage() != null) {
167                 anticipatedLength += literal.getImage().length() - 2;
168             }
169         }
170
171         return anticipatedLength;
172     }
173
174     private static final boolean isLiteral(String JavaDoc str) {
175         if (str.length() == 0) {
176             return false;
177         }
178         char c = str.charAt(0);
179         return (c == '"' || c == '\'');
180     }
181
182     private int processNode(SimpleNode sn) {
183         int anticipatedLength = 0;
184         ASTPrimaryPrefix xn = (ASTPrimaryPrefix) sn.getFirstChildOfType(ASTPrimaryPrefix.class);
185         if (xn.jjtGetNumChildren() != 0 && xn.jjtGetChild(0).getClass().equals(ASTLiteral.class)) {
186             String JavaDoc str = ((SimpleNode) xn.jjtGetChild(0)).getImage();
187             if(isLiteral(str)){
188                 anticipatedLength += str.length() - 2;
189             } else if(str.startsWith("0x")){
190                 anticipatedLength += 1;
191             } else {
192                 anticipatedLength += str.length();
193             }
194         }
195         return anticipatedLength;
196     }
197
198     private int getConstructorLength(SimpleNode node, int constructorLength) {
199         int iConstructorLength = constructorLength;
200         SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class);
201         List JavaDoc literal;
202
203         if (block == null) {
204             block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class);
205         }
206         if (block == null) {
207             block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class);
208             if (block != null) {
209                 iConstructorLength = -1;
210             }
211         }
212         literal = (block.findChildrenOfType(ASTLiteral.class));
213         if (literal.isEmpty()) {
214             List JavaDoc name = (block.findChildrenOfType(ASTName.class));
215             if (!name.isEmpty()) {
216                 iConstructorLength = -1;
217             }
218         } else if (literal.size() == 1) {
219             String JavaDoc str = ((SimpleNode) literal.get(0)).getImage();
220             if (str == null) {
221                 iConstructorLength = 0;
222             } else if (isLiteral(str)) {
223                 // since it's not taken into account
224
// anywhere. only count the extra 16
225
// characters
226
iConstructorLength = 14 + str.length(); // don't add the constructor's length,
227
} else {
228                 iConstructorLength = Integer.parseInt(str);
229             }
230         } else {
231             iConstructorLength = -1;
232         }
233         
234         if(iConstructorLength == 0){
235             iConstructorLength = 16;
236         }
237
238         return iConstructorLength;
239     }
240
241
242     private int getInitialLength(SimpleNode node) {
243         SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class);
244         List JavaDoc literal;
245
246         if (block == null) {
247             block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class);
248             if (block == null) {
249                 block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class);
250             }
251         }
252         literal = (block.findChildrenOfType(ASTLiteral.class));
253         if (literal.size() == 1) {
254             String JavaDoc str = ((SimpleNode) literal.get(0)).getImage();
255             if (str != null && isLiteral(str)) {
256                 return str.length() - 2; // take off the quotes
257
}
258         }
259         
260         return 0;
261     }
262
263     private boolean isAdditive(SimpleNode n) {
264         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     private Node getFirstParentBlock(Node node) {
276         Node parentNode = node.jjtGetParent();
277
278         Node lastNode = node;
279         while (parentNode != null && !blockParents.contains(parentNode.getClass())) {
280             lastNode = parentNode;
281             parentNode = parentNode.jjtGetParent();
282         }
283         if (parentNode != null && ASTIfStatement.class.equals(parentNode.getClass())) {
284             parentNode = lastNode;
285         } else if (parentNode != null && parentNode.getClass().equals(ASTSwitchStatement.class)) {
286             parentNode = getSwitchParent(parentNode, lastNode);
287         }
288         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     private static Node getSwitchParent(Node parentNode, Node lastNode) {
301         int allChildren = parentNode.jjtGetNumChildren();
302         ASTSwitchLabel label = null;
303         for (int ix = 0; ix < allChildren; ix++) {
304             Node n = parentNode.jjtGetChild(ix);
305             if (n.getClass().equals(ASTSwitchLabel.class)) {
306                 label = (ASTSwitchLabel) n;
307             } else if (n.equals(lastNode)) {
308                 parentNode = label;
309                 break;
310             }
311         }
312         return parentNode;
313     }
314
315 }
Popular Tags