1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules;
5
6 import net.sourceforge.pmd.AbstractRule;
7 import net.sourceforge.pmd.ast.ASTBlock;
8 import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
9 import net.sourceforge.pmd.ast.ASTCompilationUnit;
10 import net.sourceforge.pmd.ast.ASTImportDeclaration;
11 import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
12 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
13 import net.sourceforge.pmd.ast.ASTName;
14 import net.sourceforge.pmd.ast.ASTReferenceType;
15 import net.sourceforge.pmd.ast.ASTTryStatement;
16 import net.sourceforge.pmd.ast.ASTType;
17 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
18 import net.sourceforge.pmd.ast.Node;
19
20 import java.util.ArrayList;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Set;
25 import java.util.StringTokenizer;
26 import java.util.Vector;
27
28
29 /***
30 * Makes sure you close your database connections. It does this by
31 * looking for code patterned like this:
32 * <pre>
33 * Connection c = X;
34 * try {
35 * // do stuff, and maybe catch something
36 * } finally {
37 * c.close();
38 * }
39 * </pre>
40 */
41 public class CloseResource extends AbstractRule {
42
43 private Set types = new HashSet();
44
45 public Object visit(ASTCompilationUnit node, Object data) {
46 if (!importsJavaSqlPackage(node)) {
47 return data;
48 }
49 if (types.isEmpty()) {
50 for (StringTokenizer st = new StringTokenizer(getStringProperty("types"), ","); st.hasMoreTokens();) {
51 types.add(st.nextToken());
52 }
53 }
54 return super.visit(node, data);
55 }
56
57 public Object visit(ASTMethodDeclaration node, Object data) {
58 List vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
59 List ids = new Vector();
60
61
62 for (Iterator it = vars.iterator(); it.hasNext();) {
63 ASTLocalVariableDeclaration var = (ASTLocalVariableDeclaration) it.next();
64 ASTType type = (ASTType) var.jjtGetChild(0);
65
66 if (type.jjtGetChild(0) instanceof ASTReferenceType) {
67 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
68 if (ref.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
69 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
70 if (types.contains(clazz.getImage())) {
71 ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var.jjtGetChild(1).jjtGetChild(0);
72 ids.add(id);
73 }
74 }
75 }
76 }
77
78
79 for (int i = 0; i < ids.size(); i++) {
80 ASTVariableDeclaratorId x = (ASTVariableDeclaratorId) ids.get(i);
81 ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent().jjtGetParent(), x, data);
82 }
83 return data;
84 }
85
86 private void ensureClosed(ASTLocalVariableDeclaration var,
87 ASTVariableDeclaratorId id, Object data) {
88
89
90 String target = id.getImage() + ".close";
91 Node n = var;
92
93 while (!((n = n.jjtGetParent()) instanceof ASTBlock)) ;
94
95 ASTBlock top = (ASTBlock) n;
96
97 List tryblocks = new Vector();
98 top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
99
100 boolean closed = false;
101
102
103
104
105 for (Iterator it = tryblocks.iterator(); it.hasNext();) {
106 ASTTryStatement t = (ASTTryStatement) it.next();
107
108 if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
109 ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0);
110 List names = new ArrayList();
111 f.findChildrenOfType(ASTName.class, names, true);
112 for (Iterator it2 = names.iterator(); it2.hasNext();) {
113 if (((ASTName) it2.next()).getImage().equals(target)) {
114 closed = true;
115 }
116 }
117 }
118 }
119
120
121 if (!closed) {
122 ASTType type = (ASTType) var.jjtGetChild(0);
123 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
124 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
125 addViolation(data, id, clazz.getImage());
126 }
127 }
128
129 private boolean importsJavaSqlPackage(ASTCompilationUnit node) {
130 List nodes = node.findChildrenOfType(ASTImportDeclaration.class);
131 for (Iterator i = nodes.iterator(); i.hasNext();) {
132 ASTImportDeclaration n = (ASTImportDeclaration) i.next();
133 if (n.getPackageName().startsWith("java.sql")) {
134 return true;
135 }
136 }
137 return false;
138 }
139
140 }