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.PropertyDescriptor;
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.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 import net.sourceforge.pmd.properties.StringProperty;
20
21 import java.util.ArrayList;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.StringTokenizer;
27
28 /**
29 * Makes sure you close your database connections. It does this by
30 * looking for code patterned like this:
31 * <pre>
32 * Connection c = X;
33 * try {
34 * // do stuff, and maybe catch something
35 * } finally {
36 * c.close();
37 * }
38 *
39 * @author original author unknown
40 * @author Contribution from Pierre Mathien
41 * </pre>
42 */
43 public class CloseResource extends AbstractRule {
44
45 private Set<String> types = new HashSet<String>();
46
47 private Set<String> closeTargets = new HashSet<String>();
48 private static final PropertyDescriptor closeTargetsDescriptor = new StringProperty("closeTargets",
49 "Methods which may close this resource", "", 1.0f);
50
51 private static final PropertyDescriptor typesDescriptor = new StringProperty("types",
52 "Types that are affected by this rule", "", 2.0f);
53
54 private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap(new PropertyDescriptor[] { typesDescriptor, closeTargetsDescriptor });
55
56 protected Map<String, PropertyDescriptor> propertiesByName() {
57 return propertyDescriptorsByName;
58 }
59
60 public Object visit(ASTCompilationUnit node, Object data) {
61 if (closeTargets.isEmpty() && getStringProperty(closeTargetsDescriptor) != null) {
62 for (StringTokenizer st = new StringTokenizer(getStringProperty(closeTargetsDescriptor), ","); st.hasMoreTokens();) {
63 closeTargets.add(st.nextToken());
64 }
65 }
66 if (types.isEmpty() && getStringProperty(typesDescriptor) != null) {
67 for (StringTokenizer st = new StringTokenizer(getStringProperty(typesDescriptor), ","); st.hasMoreTokens();) {
68 types.add(st.nextToken());
69 }
70 }
71 return super.visit(node, data);
72 }
73
74 public Object visit(ASTMethodDeclaration node, Object data) {
75 List<ASTLocalVariableDeclaration> vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
76 List<ASTVariableDeclaratorId> ids = new ArrayList<ASTVariableDeclaratorId>();
77
78
79 for (ASTLocalVariableDeclaration var: vars) {
80 ASTType type = var.getTypeNode();
81
82 if (type.jjtGetChild(0) instanceof ASTReferenceType) {
83 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
84 if (ref.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
85 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
86 if (types.contains(clazz.getImage())) {
87 ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var.jjtGetChild(1).jjtGetChild(0);
88 ids.add(id);
89 }
90 }
91 }
92 }
93
94
95 for (ASTVariableDeclaratorId x : ids) {
96 ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent().jjtGetParent(), x, data);
97 }
98 return data;
99 }
100
101 private void ensureClosed(ASTLocalVariableDeclaration var,
102 ASTVariableDeclaratorId id, Object data) {
103
104
105 String target = id.getImage() + ".close";
106 Node n = var;
107
108 while (!(n instanceof ASTBlock)) {
109 n = n.jjtGetParent();
110 }
111
112 ASTBlock top = (ASTBlock) n;
113
114 List<ASTTryStatement> tryblocks = new ArrayList<ASTTryStatement>();
115 top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
116
117 boolean closed = false;
118
119
120
121
122 for (ASTTryStatement t : tryblocks) {
123 if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
124 ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0);
125 List<ASTName> names = new ArrayList<ASTName>();
126 f.findChildrenOfType(ASTName.class, names, true);
127 for (ASTName oName : names) {
128 String name = oName.getImage();
129 if (name.equals(target) || closeTargets.contains(name)) {
130 closed = true;
131 }
132 }
133 }
134 }
135
136
137 if (!closed) {
138 ASTType type = (ASTType) var.jjtGetChild(0);
139 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
140 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
141 addViolation(data, id, clazz.getImage());
142 }
143 }
144 }