UnusedPrivateMethodRule.java
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 = ((ClassScopenode.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 = (ASTMethodDeclaratormnd.getNode();
90         return ((AccessNodenode.jjtGetParent()).isPrivate() && !node.hasImageEqualTo("readObject"&& !node.hasImageEqualTo("writeObject"&& !node.hasImageEqualTo("readResolve"&& !node.hasImageEqualTo("writeReplace");
91     }
92 }