View Javadoc

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