1 package net.sourceforge.pmd.lang.java.rule.optimizations;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import net.sourceforge.pmd.lang.ast.Node;
7 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
8 import net.sourceforge.pmd.lang.java.ast.ASTForInit;
9 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
10 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
11 import net.sourceforge.pmd.lang.java.ast.ASTName;
12 import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
13 import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement;
14 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
15 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
16 import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode;
17 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
18
19
20
21
22
23
24
25
26 public class PrematureDeclarationRule extends AbstractJavaRule {
27
28 public PrematureDeclarationRule() { }
29
30
31
32
33
34
35
36
37 public Object visit(ASTLocalVariableDeclaration node, Object data) {
38
39
40 if (node.jjtGetParent().getClass().equals(ASTForInit.class)) {
41 return visit((AbstractJavaNode) node, data);
42 }
43
44 String varName = varNameIn(node);
45
46 AbstractJavaNode grandparent = (AbstractJavaNode)node.jjtGetParent().jjtGetParent();
47
48 List<Node> nextBlocks = blocksAfter(grandparent, node);
49
50 ASTBlockStatement statement;
51
52 for (Node block : nextBlocks) {
53
54 statement = (ASTBlockStatement)block;
55
56 if (hasReferencesIn(statement, varName)) break;
57
58 if (hasExit(statement)) {
59 addViolation(data, node, varName);
60 break;
61 }
62 }
63
64 return visit((AbstractJavaNode) node, data);
65 }
66
67
68
69
70
71
72
73
74
75
76 public static boolean hasAsParentBetween(Node node, Class<?> intermediateParentClass, Node topParent) {
77
78 Node currentParent = node.jjtGetParent();
79
80 while (currentParent != topParent) {
81 currentParent = currentParent.jjtGetParent();
82 if (currentParent.getClass().equals(intermediateParentClass)) return true;
83 }
84 return false;
85 }
86
87
88
89
90
91
92
93
94 @SuppressWarnings({ "rawtypes", "unchecked" })
95 private boolean hasExit(ASTBlockStatement block) {
96
97 List exitBlocks = block.findDescendantsOfType(ASTReturnStatement.class);
98 exitBlocks.addAll(block.findDescendantsOfType(ASTThrowStatement.class));
99
100 if (exitBlocks.isEmpty()) return false;
101
102
103 for (int i=0; i<exitBlocks.size(); i++) {
104 Node exitNode = (Node)exitBlocks.get(i);
105 if (hasAsParentBetween(exitNode, ASTMethodDeclaration.class, block)) continue;
106 return true;
107 }
108
109 return false;
110 }
111
112
113
114
115
116
117
118
119
120 private static boolean hasReferencesIn(ASTBlockStatement block, String varName) {
121
122 List<ASTName> names = block.findDescendantsOfType(ASTName.class);
123
124 for (ASTName name : names) {
125 if (isReference(varName, name.getImage())) return true;
126 }
127 return false;
128 }
129
130
131
132
133
134
135
136
137
138 private static boolean isReference(String shortName, String compoundName) {
139
140 int dotPos = compoundName.indexOf('.');
141
142 return dotPos < 0 ?
143 shortName.equals(compoundName) :
144 shortName.endsWith(compoundName.substring(0, dotPos));
145 }
146
147
148
149
150
151
152
153 private static String varNameIn(ASTLocalVariableDeclaration node) {
154 ASTVariableDeclarator declarator = node.getFirstChildOfType(ASTVariableDeclarator.class);
155 return ((ASTVariableDeclaratorId) declarator.jjtGetChild(0)).getImage();
156 }
157
158
159
160
161
162
163
164
165 private static int indexOf(AbstractJavaNode block, Node node) {
166
167 int count = block.jjtGetNumChildren();
168
169 for (int i=0; i<count; i++) {
170 if (node == block.jjtGetChild(i)) return i;
171 }
172
173 return -1;
174 }
175
176
177
178
179
180
181
182
183
184 private static List<Node> blocksAfter(AbstractJavaNode block, AbstractJavaNode node) {
185
186 int count = block.jjtGetNumChildren();
187 int start = indexOf(block, node.jjtGetParent()) + 1;
188
189 List<Node> nextBlocks = new ArrayList<Node>(count);
190
191 for (int i=start; i<count; i++) {
192 nextBlocks.add(block.jjtGetChild(i));
193 }
194
195 return nextBlocks;
196 }
197 }