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.ast.ASTArguments;
9 import net.sourceforge.pmd.ast.ASTClassBody;
10 import net.sourceforge.pmd.ast.ASTCompilationUnit;
11 import net.sourceforge.pmd.ast.ASTInterfaceDeclaration;
12 import net.sourceforge.pmd.ast.ASTMethodDeclarator;
13 import net.sourceforge.pmd.ast.ASTName;
14 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
15 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
16 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
17 import net.sourceforge.pmd.ast.AccessNode;
18 import net.sourceforge.pmd.ast.SimpleNode;
19
20 import java.text.MessageFormat;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Set;
24
25 public class UnusedPrivateMethodRule extends AbstractRule {
26
27 private Set privateMethodNodes = new HashSet();
28
29 // TODO - What I need is a Visitor that does a breadth first search
30 private boolean trollingForDeclarations;
31 private int depth;
32
33 // Skip interfaces because they have no implementation
34 public Object visit(ASTInterfaceDeclaration node, Object data) {
35 return data;
36 }
37
38 // Reset state when we leave an ASTCompilationUnit
39 public Object visit(ASTCompilationUnit node, Object data) {
40 depth = 0;
41 super.visit(node, data);
42 privateMethodNodes.clear();
43 depth = 0;
44 trollingForDeclarations = false;
45 return data;
46 }
47
48 public Object visit(ASTClassBody node, Object data) {
49 depth++;
50
51 // first troll for declarations, but only in the top level class
52 if (depth == 1) {
53 trollingForDeclarations = true;
54 super.visit(node, null);
55 trollingForDeclarations = false;
56 } else {
57 trollingForDeclarations = false;
58 }
59
60 // troll for usages, regardless of depth
61 super.visit(node, null);
62
63 // if we're back at the top level class, harvest
64 if (depth == 1) {
65 RuleContext ctx = (RuleContext) data;
66 harvestUnused(ctx);
67 }
68
69 depth--;
70 return data;
71 }
72
73 //ASTMethodDeclarator
74 // FormalParameters
75 // FormalParameter
76 // FormalParameter
77 public Object visit(ASTMethodDeclarator node, Object data) {
78 if (!trollingForDeclarations) {
79 return super.visit(node, data);
80 }
81
82 AccessNode parent = (AccessNode) node.jjtGetParent();
83 if (!parent.isPrivate()) {
84 return super.visit(node, data);
85 }
86 // exclude these serializable things
87 if (node.getImage().equals("readObject") || node.getImage().equals("writeObject") || node.getImage().equals("readResolve") || node.getImage().equals("writeReplace")) {
88 return super.visit(node, data);
89 }
90 privateMethodNodes.add(node);
91 return super.visit(node, data);
92 }
93
94 //PrimarySuffix
95 // Arguments
96 // ArgumentList
97 // Expression
98 // Expression
99 public Object visit(ASTPrimarySuffix node, Object data) {
100 if (!trollingForDeclarations && (node.jjtGetParent() instanceof ASTPrimaryExpression) && (node.getImage() != null)) {
101 if (node.jjtGetNumChildren() > 0) {
102 ASTArguments args = (ASTArguments) node.jjtGetChild(0);
103 removeIfUsed(node.getImage(), args.getArgumentCount());
104 return super.visit(node, data);
105 }
106 // to handle this.foo()
107 //PrimaryExpression
108 // PrimaryPrefix
109 // PrimarySuffix <-- this node has "foo"
110 // PrimarySuffix <-- this node has null
111 // Arguments
112 ASTPrimaryExpression parent = (ASTPrimaryExpression) node.jjtGetParent();
113 int pointer = 0;
114 while (true) {
115 if (parent.jjtGetChild(pointer).equals(node)) {
116 break;
117 }
118 pointer++;
119 }
120 // now move to the next PrimarySuffix and get the number of arguments
121 pointer++;
122 // this.foo = foo;
123 // yields this:
124 // PrimaryExpression
125 // PrimaryPrefix
126 // PrimarySuffix
127 // so we check for that
128 if (parent.jjtGetNumChildren() <= pointer) {
129 return super.visit(node, data);
130 }
131 if (!(parent.jjtGetChild(pointer) instanceof ASTPrimarySuffix)) {
132 return super.visit(node, data);
133 }
134 ASTPrimarySuffix actualMethodNode = (ASTPrimarySuffix) parent.jjtGetChild(pointer);
135 // when does this happen?
136 if (actualMethodNode.jjtGetNumChildren() == 0 || !(actualMethodNode.jjtGetChild(0) instanceof ASTArguments)) {
137 return super.visit(node, data);
138 }
139 ASTArguments args = (ASTArguments) actualMethodNode.jjtGetChild(0);
140 removeIfUsed(node.getImage(), args.getArgumentCount());
141 // what about Outer.this.foo()?
142 }
143 return super.visit(node, data);
144 }
145
146 //PrimaryExpression
147 // PrimaryPrefix
148 // Name
149 // PrimarySuffix
150 // Arguments
151 public Object visit(ASTName node, Object data) {
152 if (!trollingForDeclarations && (node.jjtGetParent() instanceof ASTPrimaryPrefix)) {
153 ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node.jjtGetParent().jjtGetParent();
154 if (primaryExpression.jjtGetNumChildren() > 1) {
155 ASTPrimarySuffix primarySuffix = (ASTPrimarySuffix) primaryExpression.jjtGetChild(1);
156 if (primarySuffix.jjtGetNumChildren() > 0 && (primarySuffix.jjtGetChild(0) instanceof ASTArguments)) {
157 ASTArguments arguments = (ASTArguments) primarySuffix.jjtGetChild(0);
158 removeIfUsed(node.getImage(), arguments.getArgumentCount());
159 }
160 }
161 }
162 return super.visit(node, data);
163 }
164
165 private void removeIfUsed(String nodeImage, int args) {
166 String img = (nodeImage.indexOf('.') == -1) ? nodeImage : nodeImage.substring(nodeImage.indexOf('.') + 1, nodeImage.length());
167 for (Iterator i = privateMethodNodes.iterator(); i.hasNext();) {
168 ASTMethodDeclarator methodNode = (ASTMethodDeclarator) i.next();
169 // are name and number of parameters the same?
170 if (methodNode.getImage().equals(img) && methodNode.getParameterCount() == args) {
171 // should check parameter types here, this misses some unused methods
172 i.remove();
173 }
174 }
175 }
176
177 private void harvestUnused(RuleContext ctx) {
178 for (Iterator i = privateMethodNodes.iterator(); i.hasNext();) {
179 SimpleNode node = (SimpleNode) i.next();
180 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine(), MessageFormat.format(getMessage(), new Object[]{node.getImage()})));
181 }
182 }
183
184 /*
185 TODO this uses the symbol table
186 public Object visit(ASTUnmodifiedClassDeclaration node, Object data) {
187 for (Iterator i = node.getScope().getUnusedMethodDeclarations();i.hasNext();) {
188 VariableNameDeclaration decl = (VariableNameDeclaration)i.next();
189
190 // exclude non-private methods and serializable methods
191 if (!decl.getAccessNodeParent().isPrivate() || decl.getImage().equals("readObject") || decl.getImage().equals("writeObject")|| decl.getImage().equals("readResolve")) {
192 continue;
193 }
194
195 RuleContext ctx = (RuleContext)data;
196 ctx.getReport().addRuleViolation(createRuleViolation(ctx, decl.getNode().getBeginLine(), MessageFormat.format(getMessage(), new Object[] {decl.getNode().getImage()})));
197 }
198 return super.visit(node, data);
199 }
200
201 */
202 }
This page was automatically generated by Maven