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.symboltable.VariableNameDeclaration;
9 import net.sourceforge.pmd.ast.ASTArguments;
10 import net.sourceforge.pmd.ast.ASTClassOrInterfaceBody;
11 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
12 import net.sourceforge.pmd.ast.ASTCompilationUnit;
13 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
14 import net.sourceforge.pmd.ast.ASTMethodDeclarator;
15 import net.sourceforge.pmd.ast.ASTName;
16 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
17 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
18 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
19 import net.sourceforge.pmd.ast.AccessNode;
20 import net.sourceforge.pmd.ast.SimpleNode;
21
22 import java.text.MessageFormat;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Set;
26
27 public class UnusedPrivateMethodRule extends AbstractRule {
28
29 private final Set privateMethodNodes = new HashSet();
30
31
32 private boolean trollingForDeclarations;
33 private int depth;
34
35
36 public Object visit(ASTCompilationUnit node, Object data) {
37 depth = 0;
38 super.visit(node, data);
39 privateMethodNodes.clear();
40 depth = 0;
41 trollingForDeclarations = false;
42 return data;
43 }
44
45 public Object visit(ASTClassOrInterfaceBody node, Object data) {
46 if (node.jjtGetParent() instanceof ASTClassOrInterfaceDeclaration) {
47 if (((ASTClassOrInterfaceDeclaration)node.jjtGetParent()).isInterface()) {
48 return data;
49 }
50 }
51
52 depth++;
53
54
55 if (depth == 1) {
56 trollingForDeclarations = true;
57 super.visit(node, null);
58 trollingForDeclarations = false;
59 } else {
60 trollingForDeclarations = false;
61 }
62
63
64 super.visit(node, null);
65
66
67 if (depth == 1) {
68 RuleContext ctx = (RuleContext) data;
69 harvestUnused(ctx);
70 }
71
72 depth--;
73 return data;
74 }
75
76 public Object visit(ASTMethodDeclarator node, Object data) {
77 if (!trollingForDeclarations) {
78 return super.visit(node, data);
79 }
80
81 AccessNode parent = (AccessNode) node.jjtGetParent();
82 if (!parent.isPrivate()) {
83 return super.visit(node, data);
84 }
85
86 if (node.getImage().equals("readObject") || node.getImage().equals("writeObject") || node.getImage().equals("readResolve") || node.getImage().equals("writeReplace")) {
87 return super.visit(node, data);
88 }
89 privateMethodNodes.add(node);
90 return super.visit(node, data);
91 }
92
93 public Object visit(ASTPrimarySuffix node, Object data) {
94 if (!trollingForDeclarations && (node.jjtGetParent() instanceof ASTPrimaryExpression) && (node.getImage() != null)) {
95 if (node.jjtGetNumChildren() > 0) {
96 ASTArguments args = (ASTArguments) node.jjtGetChild(0);
97 removeIfUsed(node, args.getArgumentCount());
98 return super.visit(node, data);
99 }
100
101
102
103
104
105
106 ASTPrimaryExpression parent = (ASTPrimaryExpression) node.jjtGetParent();
107 int pointer = 0;
108 while (true) {
109 if (parent.jjtGetChild(pointer).equals(node)) {
110 break;
111 }
112 pointer++;
113 }
114
115 pointer++;
116
117
118
119
120
121
122 if (parent.jjtGetNumChildren() <= pointer) {
123 return super.visit(node, data);
124 }
125 if (!(parent.jjtGetChild(pointer) instanceof ASTPrimarySuffix)) {
126 return super.visit(node, data);
127 }
128 ASTPrimarySuffix actualMethodNode = (ASTPrimarySuffix) parent.jjtGetChild(pointer);
129
130 if (actualMethodNode.jjtGetNumChildren() == 0 || !(actualMethodNode.jjtGetChild(0) instanceof ASTArguments)) {
131 return super.visit(node, data);
132 }
133 ASTArguments args = (ASTArguments) actualMethodNode.jjtGetChild(0);
134 removeIfUsed(node, args.getArgumentCount());
135
136 }
137 return super.visit(node, data);
138 }
139
140 public Object visit(ASTName node, Object data) {
141 if (!trollingForDeclarations && (node.jjtGetParent() instanceof ASTPrimaryPrefix)) {
142 ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node.jjtGetParent().jjtGetParent();
143 if (primaryExpression.jjtGetNumChildren() > 1) {
144 ASTPrimarySuffix primarySuffix = (ASTPrimarySuffix) primaryExpression.jjtGetChild(1);
145 if (primarySuffix.jjtGetNumChildren() > 0 && (primarySuffix.jjtGetChild(0) instanceof ASTArguments)) {
146 ASTArguments arguments = (ASTArguments) primarySuffix.jjtGetChild(0);
147 removeIfUsed(node, arguments.getArgumentCount());
148 }
149 }
150 }
151 return super.visit(node, data);
152 }
153
154 private final void removeIfUsed(SimpleNode node, int args) {
155 String img = (node.getImage().indexOf('.') == -1) ? node.getImage() : node.getImage().substring(node.getImage().indexOf('.') + 1, node.getImage().length());
156 for (Iterator i = privateMethodNodes.iterator(); i.hasNext();) {
157 ASTMethodDeclarator methodNode = (ASTMethodDeclarator) i.next();
158
159 if (methodNode.getImage().equals(img)
160 && methodNode.getParameterCount() == args
161 && !methodCalledFromItself(node, node.getImage())) {
162
163 i.remove();
164 }
165 }
166 }
167
168 /***
169 * This checks that the nodeImage is not the name of the enclosing method
170 */
171 private final boolean methodCalledFromItself(SimpleNode node, String nodeImage) {
172 final ASTMethodDeclaration md = (ASTMethodDeclaration) node.getFirstParentOfType(ASTMethodDeclaration.class);
173 if (md!=null) {
174 final ASTMethodDeclarator dcl = (ASTMethodDeclarator) md.getFirstChildOfType(ASTMethodDeclarator.class);
175 if (dcl!=null && dcl.getImage()!=null&&dcl.getImage().equals(nodeImage)) {
176 return true;
177 }
178 }
179 return false;
180 }
181
182 private final void harvestUnused(RuleContext ctx) {
183 SimpleNode node;
184 for (Iterator i = privateMethodNodes.iterator(); i.hasNext();) {
185 node = (SimpleNode) i.next();
186 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node, MessageFormat.format(getMessage(), new Object[]{node.getImage()})));
187 }
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 }