/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.rules;
import net.sourceforge.pmd.AbstractRule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.ast.ASTBlock;
import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.ast.ASTName;
import net.sourceforge.pmd.ast.ASTTryStatement;
import net.sourceforge.pmd.ast.ASTType;
import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.ast.Node;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
* Makes sure you close your database connections. It does this by
* looking for code patterned like this:
* <pre>
* Connection c = X;
* try {
* // do stuff, and maybe catch something
* } finally {
* c.close();
* }
* </pre>
*/
public class CloseConnectionRule extends AbstractRule {
public Object visit(ASTMethodDeclaration node, Object data) {
List vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
List ids = new Vector();
// find all variable references to Connection objects
for (Iterator it = vars.iterator(); it.hasNext();) {
ASTLocalVariableDeclaration var = (ASTLocalVariableDeclaration) it.next();
ASTType type = (ASTType) var.jjtGetChild(0);
if (type.jjtGetChild(0) instanceof ASTName && ((ASTName) type.jjtGetChild(0)).getImage().equals("Connection")) {
ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var.jjtGetChild(1).jjtGetChild(0);
ids.add(id);
}
}
// if there are connections, ensure each is closed.
for (int i = 0; i < ids.size(); i++) {
ASTVariableDeclaratorId x = (ASTVariableDeclaratorId) ids.get(i);
ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent()
.jjtGetParent(), x, data);
}
return data;
}
private void ensureClosed(ASTLocalVariableDeclaration var,
ASTVariableDeclaratorId id, Object data) {
// What are the chances of a Connection being instantiated in a
// for-loop init block? Anyway, I'm lazy!
String target = id.getImage() + ".close";
Node n = var;
while (!((n = n.jjtGetParent()) instanceof ASTBlock))
;
ASTBlock top = (ASTBlock) n;
List tryblocks = new Vector();
top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
boolean closed = false;
// look for try blocks below the line the variable was
// introduced and make sure there is a .close call in a finally
// block.
for (Iterator it = tryblocks.iterator(); it.hasNext();) {
ASTTryStatement t = (ASTTryStatement) it.next();
if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
ASTBlock f = t.getFinallyBlock();
List names = new ArrayList();
f.findChildrenOfType(ASTName.class, names, true);
for (Iterator it2 = names.iterator(); it2.hasNext();) {
if (((ASTName) it2.next()).getImage().equals(target)) {
closed = true;
}
}
}
}
// if all is not well, complain
if (!closed) {
RuleContext ctx = (RuleContext) data;
ctx.getReport().addRuleViolation(createRuleViolation(ctx, id.getBeginLine(), getMessage()));
}
}
}