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