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