01 /**
02 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
03 */
04 package net.sourceforge.pmd.lang.java.rule.unusedcode;
05
06 import java.util.HashSet;
07 import java.util.List;
08 import java.util.Map;
09 import java.util.Set;
10
11 import net.sourceforge.pmd.lang.ast.Node;
12 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
13 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
14 import net.sourceforge.pmd.lang.java.ast.ASTInitializer;
15 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
16 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
17 import net.sourceforge.pmd.lang.java.ast.AccessNode;
18 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
19 import net.sourceforge.pmd.lang.java.symboltable.ClassScope;
20 import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
21 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
22
23 public class UnusedPrivateMethodRule extends AbstractJavaRule {
24
25
26 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
27 if (node.isInterface()) {
28 return data;
29 }
30
31 Map<MethodNameDeclaration, List<NameOccurrence>> methods = ((ClassScope) node.getScope()).getMethodDeclarations();
32 for (MethodNameDeclaration mnd: findUnique(methods)) {
33 List<NameOccurrence> occs = methods.get(mnd);
34 if (!privateAndNotExcluded(mnd)) {
35 continue;
36 }
37 if (occs.isEmpty()) {
38 addViolation(data, mnd.getNode(), mnd.getImage() + mnd.getParameterDisplaySignature());
39 } else {
40 if (calledFromOutsideItself(occs, mnd)) {
41 addViolation(data, mnd.getNode(), mnd.getImage() + mnd.getParameterDisplaySignature());
42 }
43
44 }
45 }
46 return data;
47 }
48
49 private Set<MethodNameDeclaration> findUnique(Map<MethodNameDeclaration, List<NameOccurrence>> methods) {
50 // some rather hideous hackery here
51 // to work around the fact that PMD does not yet do full type analysis
52 // when it does, delete this
53 Set<MethodNameDeclaration> unique = new HashSet<MethodNameDeclaration>();
54 Set<String> sigs = new HashSet<String>();
55 for (MethodNameDeclaration mnd: methods.keySet()) {
56 String sig = mnd.getImage() + mnd.getParameterCount() + mnd.isVarargs();
57 if (!sigs.contains(sig)) {
58 unique.add(mnd);
59 }
60 sigs.add(sig);
61 }
62 return unique;
63 }
64
65 private boolean calledFromOutsideItself(List<NameOccurrence> occs, MethodNameDeclaration mnd) {
66 int callsFromOutsideMethod = 0;
67 for (NameOccurrence occ: occs) {
68 Node occNode = occ.getLocation();
69 ASTConstructorDeclaration enclosingConstructor = occNode.getFirstParentOfType(ASTConstructorDeclaration.class);
70 if (enclosingConstructor != null) {
71 callsFromOutsideMethod++;
72 break; // Do we miss unused private constructors here?
73 }
74 ASTInitializer enclosingInitializer = occNode.getFirstParentOfType(ASTInitializer.class);
75 if (enclosingInitializer != null) {
76 callsFromOutsideMethod++;
77 break;
78 }
79
80 ASTMethodDeclaration enclosingMethod = occNode.getFirstParentOfType(ASTMethodDeclaration.class);
81 if (enclosingMethod == null || !mnd.getNode().jjtGetParent().equals(enclosingMethod)) {
82 callsFromOutsideMethod++;
83 }
84 }
85 return callsFromOutsideMethod == 0;
86 }
87
88 private boolean privateAndNotExcluded(MethodNameDeclaration mnd) {
89 ASTMethodDeclarator node = (ASTMethodDeclarator) mnd.getNode();
90 return ((AccessNode) node.jjtGetParent()).isPrivate() && !node.hasImageEqualTo("readObject") && !node.hasImageEqualTo("writeObject") && !node.hasImageEqualTo("readResolve") && !node.hasImageEqualTo("writeReplace");
91 }
92 }
|