1
2
3
4 package net.sourceforge.pmd.lang.java.rule.strings;
5
6 import java.util.List;
7 import java.util.Map;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
11 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
13 import net.sourceforge.pmd.lang.java.ast.ASTName;
14 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
15 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
16 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
17 import net.sourceforge.pmd.lang.java.ast.ASTStatement;
18 import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
19 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
20 import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode;
21 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
22 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
23 import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
24 import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class ConsecutiveAppendsShouldReuseRule extends AbstractJavaRule {
55
56 @Override
57 public Object visit(ASTBlockStatement node, Object data) {
58 String variable = getVariableAppended(node);
59 if (variable != null) {
60 ASTBlockStatement nextSibling = getNextBlockStatementSibling(node);
61 if (nextSibling != null) {
62 String nextVariable = getVariableAppended(nextSibling);
63 if (nextVariable != null && nextVariable.equals(variable)) {
64 addViolation(data, node);
65 }
66 }
67 }
68 return super.visit(node, data);
69 }
70 private ASTBlockStatement getNextBlockStatementSibling(Node node) {
71 Node parent = node.jjtGetParent();
72 int childIndex = -1;
73 for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
74 if (parent.jjtGetChild(i) == node) {
75 childIndex = i;
76 break;
77 }
78 }
79 if (childIndex + 1 < parent.jjtGetNumChildren()) {
80 Node nextSibling = parent.jjtGetChild(childIndex + 1);
81 if (nextSibling instanceof ASTBlockStatement) {
82 return (ASTBlockStatement)nextSibling;
83 }
84 }
85 return null;
86 }
87 private String getVariableAppended(ASTBlockStatement node) {
88 if (isFirstChild(node, ASTStatement.class)) {
89 ASTStatement statement = (ASTStatement) node.jjtGetChild(0);
90 if (isFirstChild(statement, ASTStatementExpression.class)) {
91 ASTStatementExpression stmtExp = (ASTStatementExpression) statement.jjtGetChild(0);
92 if (stmtExp.jjtGetNumChildren() == 1) {
93 ASTPrimaryPrefix primaryPrefix = stmtExp.getFirstDescendantOfType(ASTPrimaryPrefix.class);
94 if (primaryPrefix != null) {
95 ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class);
96 if (name != null) {
97 String image = name.getImage();
98 if (image.endsWith(".append")) {
99 String variable = image.substring(0, image.indexOf('.'));
100 if (isAStringBuilderBuffer(primaryPrefix, variable)) {
101 return variable;
102 }
103 }
104 }
105 }
106 } else {
107 final ASTExpression exp = stmtExp.getFirstDescendantOfType(ASTExpression.class);
108 if (isFirstChild(exp, ASTPrimaryExpression.class)) {
109 final ASTPrimarySuffix primarySuffix = ((ASTPrimaryExpression) exp.jjtGetChild(0)).getFirstDescendantOfType(ASTPrimarySuffix.class);
110 if (primarySuffix != null) {
111 final String name = primarySuffix.getImage();
112 if (name != null && name.equals("append")) {
113 final ASTPrimaryExpression pExp = stmtExp.getFirstDescendantOfType(ASTPrimaryExpression.class);
114 if (pExp != null) {
115 final ASTName astName = stmtExp.getFirstDescendantOfType(ASTName.class);
116 if (astName != null) {
117 final String variable = astName.getImage();
118 if (isAStringBuilderBuffer(primarySuffix, variable)) {
119 return variable;
120 }
121 }
122 }
123 }
124 }
125 }
126 }
127 }
128 } else if (isFirstChild(node, ASTLocalVariableDeclaration.class)) {
129 ASTLocalVariableDeclaration lvd = (ASTLocalVariableDeclaration) node.jjtGetChild(0);
130
131 ASTVariableDeclaratorId vdId = lvd.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
132 ASTExpression exp = lvd.getFirstDescendantOfType(ASTExpression.class);
133
134 if (exp != null) {
135 ASTPrimarySuffix primarySuffix = exp.getFirstDescendantOfType(ASTPrimarySuffix.class);
136 if (primarySuffix != null) {
137 final String name = primarySuffix.getImage();
138 if (name != null && name.equals("append")) {
139 String variable = vdId.getImage();
140 if (isAStringBuilderBuffer(primarySuffix, variable)) {
141 return variable;
142 }
143 }
144 }
145 }
146 }
147
148 return null;
149 }
150
151 private boolean isAStringBuilderBuffer(AbstractJavaNode node, String name) {
152 Map<VariableNameDeclaration, List<NameOccurrence>> declarations = node.getScope().getDeclarations(VariableNameDeclaration.class);
153 for (VariableNameDeclaration decl : declarations.keySet()) {
154 if (decl.getName().equals(name) && TypeHelper.isEither(decl, StringBuilder.class, StringBuffer.class)) {
155 return true;
156 }
157 }
158 return false;
159 }
160
161 private boolean isFirstChild(Node node, Class<?> clazz) {
162 if (node.jjtGetNumChildren() == 1 && clazz.isAssignableFrom(node.jjtGetChild(0).getClass())) {
163 return true;
164 }
165 return false;
166 }
167 }