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.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.StringTokenizer;
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
46 private Set closeTargets = new HashSet();
47 private static final PropertyDescriptor closeTargetsDescriptor = new StringProperty("closeTargets",
48 "Methods which may close this resource", "", 1.0f);
49
50 private static final PropertyDescriptor typesDescriptor = new StringProperty("types",
51 "Types that are affected by this rule", "", 2.0f);
52
53 private static final Map propertyDescriptorsByName = asFixedMap(new PropertyDescriptor[] { typesDescriptor, closeTargetsDescriptor });
54
55 protected Map propertiesByName() {
56 return propertyDescriptorsByName;
57 };
58
59 public Object visit(ASTCompilationUnit node, Object data) {
60 if (closeTargets.isEmpty() && getStringProperty(closeTargetsDescriptor) != null) {
61 for (StringTokenizer st = new StringTokenizer(getStringProperty(closeTargetsDescriptor), ","); st.hasMoreTokens();) {
62 closeTargets.add(st.nextToken());
63 }
64 }
65 if (types.isEmpty() && getStringProperty(typesDescriptor) != null) {
66 for (StringTokenizer st = new StringTokenizer(getStringProperty(typesDescriptor), ","); st.hasMoreTokens();) {
67 types.add(st.nextToken());
68 }
69 }
70 return super.visit(node, data);
71 }
72
73 public Object visit(ASTMethodDeclaration node, Object data) {
74 List vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
75 List ids = new ArrayList();
76
77
78 for (Iterator it = vars.iterator(); it.hasNext();) {
79 ASTLocalVariableDeclaration var = (ASTLocalVariableDeclaration) it.next();
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 (int i = 0; i < ids.size(); i++) {
96 ASTVariableDeclaratorId x = (ASTVariableDeclaratorId) ids.get(i);
97 ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent().jjtGetParent(), x, data);
98 }
99 return data;
100 }
101
102 private void ensureClosed(ASTLocalVariableDeclaration var,
103 ASTVariableDeclaratorId id, Object data) {
104
105
106 String target = id.getImage() + ".close";
107 Node n = var;
108
109 while (!((n = n.jjtGetParent()) instanceof ASTBlock)) ;
110
111 ASTBlock top = (ASTBlock) n;
112
113 List tryblocks = new ArrayList();
114 top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
115
116 boolean closed = false;
117
118
119
120
121 for (Iterator it = tryblocks.iterator(); it.hasNext();) {
122 ASTTryStatement t = (ASTTryStatement) it.next();
123
124 if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
125 ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0);
126 List names = new ArrayList();
127 f.findChildrenOfType(ASTName.class, names, true);
128 for (Iterator it2 = names.iterator(); it2.hasNext();) {
129 String name = ((ASTName) it2.next()).getImage();
130 if (name.equals(target) || closeTargets.contains(name)) {
131 closed = true;
132 }
133 }
134 }
135 }
136
137
138 if (!closed) {
139 ASTType type = (ASTType) var.jjtGetChild(0);
140 ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
141 ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
142 addViolation(data, id, clazz.getImage());
143 }
144 }
145 }