1
2
3
4 package net.sourceforge.pmd.lang.java.rule.design;
5
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Set;
11
12 import net.sourceforge.pmd.lang.ast.Node;
13 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
14 import net.sourceforge.pmd.lang.java.ast.ASTBlock;
15 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
16 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
17 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
18 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
19 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
20 import net.sourceforge.pmd.lang.java.ast.ASTName;
21 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
22 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
23 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
24 import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
25 import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
26 import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
27 import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
28 import net.sourceforge.pmd.lang.java.ast.ASTType;
29 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
30 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
31 import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class CloseResourceRule extends AbstractJavaRule {
49
50 private Set<String> types = new HashSet<String>();
51 private Set<String> simpleTypes = new HashSet<String>();
52
53 private Set<String> closeTargets = new HashSet<String>();
54 private static final StringMultiProperty CLOSE_TARGETS_DESCRIPTOR = new StringMultiProperty("closeTargets",
55 "Methods which may close this resource", new String[]{}, 1.0f, ',');
56
57 private static final StringMultiProperty TYPES_DESCRIPTOR = new StringMultiProperty("types",
58 "Affected types", new String[]{"java.sql.Connection","java.sql.Statement","java.sql.ResultSet"}, 2.0f, ',');
59
60 public CloseResourceRule() {
61 definePropertyDescriptor(CLOSE_TARGETS_DESCRIPTOR);
62 definePropertyDescriptor(TYPES_DESCRIPTOR);
63 }
64
65 @Override
66 public Object visit(ASTCompilationUnit node, Object data) {
67 if (closeTargets.isEmpty() && getProperty(CLOSE_TARGETS_DESCRIPTOR) != null) {
68 closeTargets.addAll(Arrays.asList(getProperty(CLOSE_TARGETS_DESCRIPTOR)));
69 }
70 if (types.isEmpty() && getProperty(TYPES_DESCRIPTOR) != null) {
71 types.addAll(Arrays.asList(getProperty(TYPES_DESCRIPTOR)));
72 }
73 if (simpleTypes.isEmpty() && getProperty(TYPES_DESCRIPTOR) != null) {
74 for (String type : getProperty(TYPES_DESCRIPTOR)) {
75 simpleTypes.add(toSimpleType(type));
76 }
77 }
78 return super.visit(node, data);
79 }
80
81 private static String toSimpleType(String fullyQualifiedClassName) {
82 int lastIndexOf = fullyQualifiedClassName.lastIndexOf('.');
83 if (lastIndexOf > -1) {
84 return fullyQualifiedClassName.substring(lastIndexOf + 1);
85 } else {
86 return fullyQualifiedClassName;
87 }
88 }
89
90 @Override
91 public Object visit(ASTConstructorDeclaration node, Object data) {
92 checkForResources(node, data);
93 return data;
94 }
95
96 @Override
97 public Object visit(ASTMethodDeclaration node, Object data) {
98 checkForResources(node, data);
99 return data;
100 }
101
102 private void checkForResources(Node node, Object data) {
103 List<ASTLocalVariableDeclaration> vars = node.findDescendantsOfType(ASTLocalVariableDeclaration.class);
104 List<ASTVariableDeclaratorId> ids = new ArrayList<ASTVariableDeclaratorId>();
105
106
107 for (ASTLocalVariableDeclaration var: vars) {
108 ASTType type = var.getTypeNode();
109
110 if (type.jjtGetChild(0) instanceof ASTReferenceType) {
111 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
112 if (ref.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
113 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
114
115 if (clazz.getType() != null && types.contains(clazz.getType().getName())
116 || (clazz.getType() == null && simpleTypes.contains(toSimpleType(clazz.getImage())))
117 || types.contains(clazz.getImage())) {
118
119 ASTVariableDeclaratorId id = var.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
120 ids.add(id);
121 }
122 }
123 }
124 }
125
126
127 for (ASTVariableDeclaratorId x : ids) {
128 ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent().jjtGetParent(), x, data);
129 }
130 }
131
132 private void ensureClosed(ASTLocalVariableDeclaration var,
133 ASTVariableDeclaratorId id, Object data) {
134
135
136 String variableToClose = id.getImage();
137 String target = variableToClose + ".close";
138 Node n = var;
139
140 while (!(n instanceof ASTBlock) && !(n instanceof ASTConstructorDeclaration)) {
141 n = n.jjtGetParent();
142 }
143
144 Node top = n;
145
146 List<ASTTryStatement> tryblocks = top.findDescendantsOfType(ASTTryStatement.class);
147
148 boolean closed = false;
149
150
151
152
153 for (ASTTryStatement t : tryblocks) {
154 if (t.getBeginLine() > id.getBeginLine() && t.hasFinally()) {
155 ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0);
156 List<ASTName> names = f.findDescendantsOfType(ASTName.class);
157 for (ASTName oName : names) {
158 String name = oName.getImage();
159 if (name.equals(target)) {
160 closed = true;
161 break;
162 }
163 }
164 if (closed) {
165 break;
166 }
167
168 List<ASTStatementExpression> exprs = new ArrayList<ASTStatementExpression>();
169 f.findDescendantsOfType(ASTStatementExpression.class, exprs, true);
170 for (ASTStatementExpression stmt : exprs) {
171 ASTPrimaryExpression expr =
172 stmt.getFirstChildOfType(ASTPrimaryExpression.class);
173 if (expr != null) {
174 ASTPrimaryPrefix prefix = expr.getFirstChildOfType(ASTPrimaryPrefix.class);
175 ASTPrimarySuffix suffix = expr.getFirstChildOfType(ASTPrimarySuffix.class);
176 if ((prefix != null) && (suffix != null)) {
177 if (prefix.getImage() == null) {
178 ASTName prefixName = prefix.getFirstChildOfType(ASTName.class);
179 if ((prefixName != null)
180 && closeTargets.contains(prefixName.getImage()))
181 {
182
183
184 closed = variableIsPassedToMethod(expr, variableToClose);
185 if (closed) {
186 break;
187 }
188 }
189 } else if (suffix.getImage() != null) {
190 String prefixPlusSuffix =
191 prefix.getImage()+ "." + suffix.getImage();
192 if (closeTargets.contains(prefixPlusSuffix)) {
193
194
195 closed = variableIsPassedToMethod(expr, variableToClose);
196 if (closed) {
197 break;
198 }
199 }
200 }
201
202
203
204
205
206 if (!closed)
207 {
208 List<ASTPrimarySuffix> suffixes = new ArrayList<ASTPrimarySuffix>();
209 expr.findDescendantsOfType(ASTPrimarySuffix.class, suffixes, true);
210 for (ASTPrimarySuffix oSuffix : suffixes) {
211 String suff = oSuffix.getImage();
212 if (closeTargets.contains(suff))
213 {
214 closed = variableIsPassedToMethod(expr, variableToClose);
215 if(closed)
216 {
217 break;
218 }
219 }
220
221 }
222 }
223 }
224 }
225 }
226 if (closed) {
227 break;
228 }
229 }
230 }
231
232 if (!closed) {
233
234
235
236 List<ASTReturnStatement> returns = new ArrayList<ASTReturnStatement>();
237 top.findDescendantsOfType(ASTReturnStatement.class, returns, true);
238 for (ASTReturnStatement returnStatement : returns) {
239 ASTName name = returnStatement.getFirstDescendantOfType(ASTName.class);
240 if ((name != null) && name.getImage().equals(variableToClose)) {
241 closed = true;
242 break;
243 }
244 }
245 }
246
247
248 if (!closed) {
249 ASTType type = var.getFirstChildOfType(ASTType.class);
250 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
251 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
252 addViolation(data, id, clazz.getImage());
253 }
254 }
255
256 private boolean variableIsPassedToMethod(ASTPrimaryExpression expr, String variable) {
257 List<ASTName> methodParams = new ArrayList<ASTName>();
258 expr.findDescendantsOfType(ASTName.class, methodParams, true);
259 for (ASTName pName : methodParams) {
260 String paramName = pName.getImage();
261
262 ASTArgumentList parentParam = pName.getFirstParentOfType(ASTArgumentList.class);
263 if (paramName.equals(variable) && parentParam != null) {
264 return true;
265 }
266 }
267 return false;
268 }
269 }