001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules;
004:
005: import net.sourceforge.pmd.AbstractRule;
006: import net.sourceforge.pmd.PropertyDescriptor;
007: import net.sourceforge.pmd.ast.ASTBlock;
008: import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
009: import net.sourceforge.pmd.ast.ASTCompilationUnit;
010: import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
011: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
012: import net.sourceforge.pmd.ast.ASTName;
013: import net.sourceforge.pmd.ast.ASTReferenceType;
014: import net.sourceforge.pmd.ast.ASTTryStatement;
015: import net.sourceforge.pmd.ast.ASTType;
016: import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
017: import net.sourceforge.pmd.ast.Node;
018: import net.sourceforge.pmd.properties.StringProperty;
019:
020: import java.util.ArrayList;
021: import java.util.HashSet;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.StringTokenizer;
026:
027: /**
028: * Makes sure you close your database connections. It does this by
029: * looking for code patterned like this:
030: * <pre>
031: * Connection c = X;
032: * try {
033: * // do stuff, and maybe catch something
034: * } finally {
035: * c.close();
036: * }
037: *
038: * @author original author unknown
039: * @author Contribution from Pierre Mathien
040: * </pre>
041: */
042: public class CloseResource extends AbstractRule {
043:
044: private Set<String> types = new HashSet<String>();
045:
046: private Set<String> closeTargets = new HashSet<String>();
047: private static final PropertyDescriptor closeTargetsDescriptor = new StringProperty(
048: "closeTargets", "Methods which may close this resource",
049: "", 1.0f);
050:
051: private static final PropertyDescriptor typesDescriptor = new StringProperty(
052: "types", "Types that are affected by this rule", "", 2.0f);
053:
054: private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap(new PropertyDescriptor[] {
055: typesDescriptor, closeTargetsDescriptor });
056:
057: protected Map<String, PropertyDescriptor> propertiesByName() {
058: return propertyDescriptorsByName;
059: }
060:
061: public Object visit(ASTCompilationUnit node, Object data) {
062: if (closeTargets.isEmpty()
063: && getStringProperty(closeTargetsDescriptor) != null) {
064: for (StringTokenizer st = new StringTokenizer(
065: getStringProperty(closeTargetsDescriptor), ","); st
066: .hasMoreTokens();) {
067: closeTargets.add(st.nextToken());
068: }
069: }
070: if (types.isEmpty()
071: && getStringProperty(typesDescriptor) != null) {
072: for (StringTokenizer st = new StringTokenizer(
073: getStringProperty(typesDescriptor), ","); st
074: .hasMoreTokens();) {
075: types.add(st.nextToken());
076: }
077: }
078: return super .visit(node, data);
079: }
080:
081: public Object visit(ASTMethodDeclaration node, Object data) {
082: List<ASTLocalVariableDeclaration> vars = node
083: .findChildrenOfType(ASTLocalVariableDeclaration.class);
084: List<ASTVariableDeclaratorId> ids = new ArrayList<ASTVariableDeclaratorId>();
085:
086: // find all variable references to Connection objects
087: for (ASTLocalVariableDeclaration var : vars) {
088: ASTType type = var.getTypeNode();
089:
090: if (type.jjtGetChild(0) instanceof ASTReferenceType) {
091: ASTReferenceType ref = (ASTReferenceType) type
092: .jjtGetChild(0);
093: if (ref.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
094: ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref
095: .jjtGetChild(0);
096: if (types.contains(clazz.getImage())) {
097: ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var
098: .jjtGetChild(1).jjtGetChild(0);
099: ids.add(id);
100: }
101: }
102: }
103: }
104:
105: // if there are connections, ensure each is closed.
106: for (ASTVariableDeclaratorId x : ids) {
107: ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent()
108: .jjtGetParent(), x, data);
109: }
110: return data;
111: }
112:
113: private void ensureClosed(ASTLocalVariableDeclaration var,
114: ASTVariableDeclaratorId id, Object data) {
115: // What are the chances of a Connection being instantiated in a
116: // for-loop init block? Anyway, I'm lazy!
117: String target = id.getImage() + ".close";
118: Node n = var;
119:
120: while (!(n instanceof ASTBlock)) {
121: n = n.jjtGetParent();
122: }
123:
124: ASTBlock top = (ASTBlock) n;
125:
126: List<ASTTryStatement> tryblocks = new ArrayList<ASTTryStatement>();
127: top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
128:
129: boolean closed = false;
130:
131: // look for try blocks below the line the variable was
132: // introduced and make sure there is a .close call in a finally
133: // block.
134: for (ASTTryStatement t : tryblocks) {
135: if ((t.getBeginLine() > id.getBeginLine())
136: && (t.hasFinally())) {
137: ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0);
138: List<ASTName> names = new ArrayList<ASTName>();
139: f.findChildrenOfType(ASTName.class, names, true);
140: for (ASTName oName : names) {
141: String name = oName.getImage();
142: if (name.equals(target)
143: || closeTargets.contains(name)) {
144: closed = true;
145: }
146: }
147: }
148: }
149:
150: // if all is not well, complain
151: if (!closed) {
152: ASTType type = (ASTType) var.jjtGetChild(0);
153: ASTReferenceType ref = (ASTReferenceType) type
154: .jjtGetChild(0);
155: ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref
156: .jjtGetChild(0);
157: addViolation(data, id, clazz.getImage());
158: }
159: }
160: }
|