Clover coverage report - PMD - 3.9 Coverage timestamp: Tue Dec 19 2006 09:38:44 EST FRAMES NO FRAMES |
file stats: | LOC: | 128 |  | Methods: | 4
| NCLOC: | 93 |  | Classes: | 1
Source file | Conditionals | Statements | Methods | TOTAL
InefficientStringBuffering.java | 88.2% | 94% | 100% | 92% |

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.ASTAllocationExpression;
9 |  | import net.sourceforge.pmd.ast.ASTArgumentList;
10 |  | import net.sourceforge.pmd.ast.ASTBlockStatement;
11 |  | import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
12 |  | import net.sourceforge.pmd.ast.ASTLiteral;
13 |  | import net.sourceforge.pmd.ast.ASTName;
14 |  | import net.sourceforge.pmd.ast.ASTStatementExpression;
15 |  | import net.sourceforge.pmd.ast.Node;
16 |  | import net.sourceforge.pmd.ast.SimpleNode;
17 |  | import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
18 |  |
19 |  | import java.util.Iterator;
20 |  | import java.util.List;
21 |  |
22 |  | /*
23 |  | * How this rule works:
24 |  | * find additive expressions: +
25 |  | * check that the addition is between anything other than two literals
26 |  | * if true and also the parent is StringBuffer constructor or append,
27 |  | * report a violation.
28 |  | *
29 |  | * @author mgriffa
30 |  | */
31 |  | public class InefficientStringBuffering extends AbstractRule {
32 |  |
33 | 20 | public Object visit(ASTAdditiveExpression node, Object data) {
34 | 20 | ASTBlockStatement bs = (ASTBlockStatement) node.getFirstParentOfType(ASTBlockStatement.class);
35 | 20 | if (bs == null) {
36 | 1 | return data;
37 |  | }
38 |  |
39 | 19 | int immediateLiterals = 0;
40 | 19 | List nodes = node.findChildrenOfType(ASTLiteral.class);
41 | 19 | for (Iterator i = nodes.iterator();i.hasNext();) {
42 | 22 | ASTLiteral literal = (ASTLiteral)i.next();
43 | 22 | if (literal.jjtGetParent().jjtGetParent().jjtGetParent() instanceof ASTAdditiveExpression) {
44 | 21 | immediateLiterals++;
45 |  | }
46 | 22 | try {
47 | 22 | Integer.parseInt(literal.getImage());
48 | 3 | return data;
49 |  | } catch (NumberFormatException nfe) {
50 |  | // NFE means new StringBuffer("a" + "b"), want to flag those
51 |  | }
52 |  | }
53 |  |
54 | 16 | if (immediateLiterals > 1) {
55 | 4 | return data;
56 |  | }
57 |  |
58 |  | // if literal + public static final, return
59 | 12 | List nameNodes = node.findChildrenOfType(ASTName.class);
60 | 12 | for (Iterator i = nameNodes.iterator(); i.hasNext();) {
61 | 15 | ASTName name = (ASTName)i.next();
62 | 15 | if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
63 | 7 | VariableNameDeclaration vnd = (VariableNameDeclaration)name.getNameDeclaration();
64 | 7 | if (vnd.getAccessNodeParent().isFinal() && vnd.getAccessNodeParent().isStatic()) {
65 | 1 | return data;
66 |  | }
67 |  | }
68 |  | }
69 |  |
70 |  |
71 | 11 | if (bs.isAllocation()) {
72 | 2 | if (isAllocatedStringBuffer(node)) {
73 | 2 | addViolation(data, node);
74 |  | }
75 | 9 | } else if (isInStringBufferOperation(node, 6, "append")) {
76 | 4 | addViolation(data, node);
77 |  | }
78 | 11 | return data;
79 |  | }
80 |  |
81 | 245 | protected static boolean isInStringBufferOperation(SimpleNode node, int length, String methodName) {
82 | 245 | if (!xParentIsStatementExpression(node, length)) {
83 | 29 | return false;
84 |  | }
85 | 216 | ASTStatementExpression s = (ASTStatementExpression) node.getFirstParentOfType(ASTStatementExpression.class);
86 | 216 | if (s == null) {
87 | 0 | return false;
88 |  | }
89 | 216 | ASTName n = (ASTName)s.getFirstChildOfType(ASTName.class);
90 | 216 | if (n == null || n.getImage().indexOf(methodName) == -1 || !(n.getNameDeclaration() instanceof VariableNameDeclaration)) {
91 | 9 | return false;
92 |  | }
93 |  |
94 |  | // TODO having to hand-code this kind of dredging around is ridiculous
95 |  | // we need something to support this in the framework
96 |  | // but, "for now" (tm):
97 |  | // if more than one arg to append(), skip it
98 | 207 | ASTArgumentList argList = (ASTArgumentList)s.getFirstChildOfType(ASTArgumentList.class);
99 | 207 | if (argList == null || argList.jjtGetNumChildren() > 1) {
100 | 3 | return false;
101 |  | }
102 |  |
103 | 204 | return ((VariableNameDeclaration)n.getNameDeclaration()).getTypeImage().equals("StringBuffer");
104 |  | }
105 |  |
106 |  | // TODO move this method to SimpleNode
107 | 245 | private static boolean xParentIsStatementExpression(SimpleNode node, int length) {
108 | 245 | Node curr = node;
109 | 245 | for (int i=0; i<length; i++) {
110 | 802 | if (node.jjtGetParent() == null) {
111 | 0 | return false;
112 |  | }
113 | 802 | curr = curr.jjtGetParent();
114 |  | }
115 | 245 | return curr instanceof ASTStatementExpression;
116 |  | }
117 |  |
118 | 2 | private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
119 | 2 | ASTAllocationExpression ao = (ASTAllocationExpression) node.getFirstParentOfType(ASTAllocationExpression.class);
120 | 2 | if (ao == null) {
121 | 0 | return false;
122 |  | }
123 |  | // note that the child can be an ArrayDimsAndInits, for example, from java.lang.FloatingDecimal: t = new int[ nWords+wordcount+1 ];
124 | 2 | ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
125 | 2 | return an != null && (an.getImage().endsWith("StringBuffer") || an.getImage().endsWith("StringBuilder"));
126 |  | }
127 |  | }
128 |  |