KickJava   Java API By Example, From Geeks To Geeks.

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


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.PropertyDescriptor;
8 import net.sourceforge.pmd.ast.ASTAdditiveExpression;
9 import net.sourceforge.pmd.ast.ASTArgumentList;
10 import net.sourceforge.pmd.ast.ASTDoStatement;
11 import net.sourceforge.pmd.ast.ASTForStatement;
12 import net.sourceforge.pmd.ast.ASTIfStatement;
13 import net.sourceforge.pmd.ast.ASTLiteral;
14 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
15 import net.sourceforge.pmd.ast.ASTName;
16 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
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.ASTWhileStatement;
22 import net.sourceforge.pmd.ast.Node;
23 import net.sourceforge.pmd.ast.SimpleNode;
24 import net.sourceforge.pmd.properties.IntegerProperty;
25 import net.sourceforge.pmd.symboltable.NameOccurrence;
26
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32
33 /**
34  * This rule finds concurrent calls to StringBuffer.append where String literals
35  * are used It would be much better to make these calls using one call to
36  * .append
37  * <p/>
38  * example:
39  * <p/>
40  * <pre>
41  * StringBuffer buf = new StringBuffer();
42  * buf.append(&quot;Hello&quot;);
43  * buf.append(&quot; &quot;).append(&quot;World&quot;);
44  * </pre>
45  * <p/>
46  * This would be more eloquently put as:
47  * <p/>
48  * <pre>
49  * StringBuffer buf = new StringBuffer();
50  * buf.append(&quot;Hello World&quot;);
51  * </pre>
52  * <p/>
53  * The rule takes one parameter, threshold, which defines the lower limit of
54  * consecutive appends before a violation is created. The default is 1.
55  */

56 public class ConsecutiveLiteralAppends extends AbstractRule {
57
58     private final static Set JavaDoc blockParents;
59
60     static {
61         blockParents = new HashSet JavaDoc();
62         blockParents.add(ASTForStatement.class);
63         blockParents.add(ASTWhileStatement.class);
64         blockParents.add(ASTDoStatement.class);
65         blockParents.add(ASTIfStatement.class);
66         blockParents.add(ASTSwitchStatement.class);
67         blockParents.add(ASTMethodDeclaration.class);
68     }
69     
70     private static final PropertyDescriptor thresholdDescriptor = new IntegerProperty(
71             "threshold",
72             "?",
73             1,
74             1.0f
75             );
76     
77     private static final Map JavaDoc propertyDescriptorsByName = asFixedMap(thresholdDescriptor);
78  
79
80     private int threshold = 1;
81
82     public Object JavaDoc visit(ASTVariableDeclaratorId node, Object JavaDoc data) {
83
84         if (!isStringBuffer(node)) {
85             return data;
86         }
87         threshold = getIntProperty(thresholdDescriptor);
88
89         int concurrentCount = checkConstructor(node, data);
90         Node lastBlock = getFirstParentBlock(node);
91         Node currentBlock = lastBlock;
92         Map JavaDoc decls = node.getScope().getVariableDeclarations();
93         SimpleNode rootNode = null;
94         // only want the constructor flagged if it's really containing strings
95
if (concurrentCount == 1) {
96             rootNode = node;
97         }
98         for (Iterator JavaDoc iter = decls.entrySet().iterator(); iter.hasNext();) {
99             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
100             List decl = (List) entry.getValue();
101             for (int ix = 0; ix < decl.size(); ix++) {
102                 NameOccurrence no = (NameOccurrence) decl.get(ix);
103                 SimpleNode n = no.getLocation();
104
105                 currentBlock = getFirstParentBlock(n);
106
107                 if (!InefficientStringBuffering.isInStringBufferOperation(n, 3,"append")) {
108                     if (!no.isPartOfQualifiedName()) {
109                         checkForViolation(rootNode, data, concurrentCount);
110                         concurrentCount = 0;
111                     }
112                     continue;
113                 }
114                 ASTPrimaryExpression s = (ASTPrimaryExpression) n
115                         .getFirstParentOfType(ASTPrimaryExpression.class);
116                 int numChildren = s.jjtGetNumChildren();
117                 for (int jx = 0; jx < numChildren; jx++) {
118                     SimpleNode sn = (SimpleNode) s.jjtGetChild(jx);
119                     if (!(sn instanceof ASTPrimarySuffix)
120                             || sn.getImage() != null) {
121                         continue;
122                     }
123
124                     // see if it changed blocks
125
if ((currentBlock != null && lastBlock != null && !currentBlock
126                             .equals(lastBlock))
127                             || (currentBlock == null ^ lastBlock == null)) {
128                         checkForViolation(rootNode, data, concurrentCount);
129                         concurrentCount = 0;
130                     }
131
132                     // if concurrent is 0 then we reset the root to report from
133
// here
134
if (concurrentCount == 0) {
135                         rootNode = sn;
136                     }
137                     if (isAdditive(sn)) {
138                         concurrentCount = processAdditive(data,
139                                 concurrentCount, sn, rootNode);
140                         if (concurrentCount != 0) {
141                             rootNode = sn;
142                         }
143                     } else if (!isAppendingStringLiteral(sn)) {
144                         checkForViolation(rootNode, data, concurrentCount);
145                         concurrentCount = 0;
146                     } else {
147                         concurrentCount++;
148                     }
149                     lastBlock = currentBlock;
150                 }
151             }
152         }
153         checkForViolation(rootNode, data, concurrentCount);
154         return data;
155     }
156
157     /**
158      * Determie if the constructor contains (or ends with) a String Literal
159      *
160      * @param node
161      * @return 1 if the constructor contains string argument, else 0
162      */

163     private int checkConstructor(ASTVariableDeclaratorId node, Object JavaDoc data) {
164         Node parent = node.jjtGetParent();
165         if (parent.jjtGetNumChildren() >= 2) {
166             ASTArgumentList list = (ASTArgumentList) ((SimpleNode) parent
167                     .jjtGetChild(1)).getFirstChildOfType(ASTArgumentList.class);
168             if (list != null) {
169                 ASTLiteral literal = (ASTLiteral) list
170                         .getFirstChildOfType(ASTLiteral.class);
171                 if (!isAdditive(list) && literal != null
172                         && literal.isStringLiteral()) {
173                     return 1;
174                 }
175                 return processAdditive(data, 0, list, node);
176             }
177         }
178         return 0;
179     }
180
181     private int processAdditive(Object JavaDoc data, int concurrentCount,
182                                 SimpleNode sn, SimpleNode rootNode) {
183         ASTAdditiveExpression additive = (ASTAdditiveExpression) sn
184                 .getFirstChildOfType(ASTAdditiveExpression.class);
185         if (additive == null) {
186             return 0;
187         }
188         int count = concurrentCount;
189         boolean found = false;
190         for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) {
191             SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix);
192             if (childNode.jjtGetNumChildren() != 1
193                     || childNode.findChildrenOfType(ASTName.class).size() != 0) {
194                 if (!found) {
195                     checkForViolation(rootNode, data, count);
196                     found = true;
197                 }
198                 count = 0;
199             } else {
200                 count++;
201             }
202         }
203
204         // no variables appended, compiler will take care of merging all the
205
// string concats, we really only have 1 then
206
if (!found) {
207             count = 1;
208         }
209
210         return count;
211     }
212
213     /**
214      * Checks to see if there is string concatenation in the node.
215      *
216      * This method checks if it's additive with respect to the append method
217      * only.
218      *
219      * @param n
220      * Node to check
221      * @return true if the node has an additive expression (i.e. "Hello " +
222      * Const.WORLD)
223      */

224     private boolean isAdditive(SimpleNode n) {
225         List lstAdditive = n.findChildrenOfType(ASTAdditiveExpression.class);
226         if (lstAdditive.isEmpty()) {
227             return false;
228         }
229         // if there are more than 1 set of arguments above us we're not in the
230
// append
231
// but a sub-method call
232
for (int ix = 0; ix < lstAdditive.size(); ix++) {
233             ASTAdditiveExpression expr = (ASTAdditiveExpression) lstAdditive.get(ix);
234             if (expr.getParentsOfType(ASTArgumentList.class).size() != 1) {
235                 return false;
236             }
237         }
238         return true;
239     }
240
241     /**
242      * Get the first parent. Keep track of the last node though. For If
243      * statements it's the only way we can differentiate between if's and else's
244      * For switches it's the only way we can differentiate between switches
245      *
246      * @param node The node to check
247      * @return The first parent block
248      */

249     private Node getFirstParentBlock(Node node) {
250         Node parentNode = node.jjtGetParent();
251
252         Node lastNode = node;
253         while (parentNode != null
254                 && !blockParents.contains(parentNode.getClass())) {
255             lastNode = parentNode;
256             parentNode = parentNode.jjtGetParent();
257         }
258         if (parentNode != null
259                 && parentNode.getClass().equals(ASTIfStatement.class)) {
260             parentNode = lastNode;
261         } else if (parentNode != null
262                 && parentNode.getClass().equals(ASTSwitchStatement.class)) {
263             parentNode = getSwitchParent(parentNode, lastNode);
264         }
265         return parentNode;
266     }
267
268     /**
269      * Determine which SwitchLabel we belong to inside a switch
270      *
271      * @param parentNode The parent node we're looking at
272      * @param lastNode The last node processed
273      * @return The parent node for the switch statement
274      */

275     private Node getSwitchParent(Node parentNode, Node lastNode) {
276         int allChildren = parentNode.jjtGetNumChildren();
277         ASTSwitchLabel label = null;
278         for (int ix = 0; ix < allChildren; ix++) {
279             Node n = parentNode.jjtGetChild(ix);
280             if (n.getClass().equals(ASTSwitchLabel.class)) {
281                 label = (ASTSwitchLabel) n;
282             } else if (n.equals(lastNode)) {
283                 parentNode = label;
284                 break;
285             }
286         }
287         return parentNode;
288     }
289
290     /**
291      * Helper method checks to see if a violation occured, and adds a
292      * RuleViolation if it did
293      */

294     private void checkForViolation(SimpleNode node, Object JavaDoc data,
295                                    int concurrentCount) {
296         if (concurrentCount > threshold) {
297             String JavaDoc[] param = {String.valueOf(concurrentCount)};
298             addViolation(data, node, param);
299         }
300     }
301
302     private boolean isAppendingStringLiteral(SimpleNode node) {
303         SimpleNode n = node;
304         while (n.jjtGetNumChildren() != 0
305                 && !n.getClass().equals(ASTLiteral.class)) {
306             n = (SimpleNode) n.jjtGetChild(0);
307         }
308         return n.getClass().equals(ASTLiteral.class);
309     }
310
311     private static boolean isStringBuffer(ASTVariableDeclaratorId node) {
312         SimpleNode nn = node.getTypeNameNode();
313         if (nn.jjtGetNumChildren() == 0) {
314             return false;
315         }
316         return "StringBuffer".equals(((SimpleNode) nn.jjtGetChild(0)).getImage());
317     }
318
319     protected Map JavaDoc propertiesByName() {
320         return propertyDescriptorsByName;
321     }
322 }
Popular Tags