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.ArrayList;
7   import java.util.List;
8   import java.util.Map;
9   
10  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
11  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
12  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
13  import net.sourceforge.pmd.lang.java.ast.ASTEnumBody;
14  import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant;
15  import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
16  import net.sourceforge.pmd.lang.java.ast.ASTName;
17  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
18  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
19  import net.sourceforge.pmd.lang.java.ast.AccessNode;
20  import net.sourceforge.pmd.lang.java.ast.JavaNode;
21  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
22  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
23  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
24  import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
25  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
26  
27  public class UnusedPrivateFieldRule extends AbstractJavaRule {
28  
29      @Override
30      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
31          Map<VariableNameDeclaration, List<NameOccurrence>> vars = node.getScope().getDeclarations(
32                  VariableNameDeclaration.class);
33          for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : vars.entrySet()) {
34              VariableNameDeclaration decl = entry.getKey();
35              AccessNode accessNodeParent = decl.getAccessNodeParent();
36              if (!accessNodeParent.isPrivate() || isOK(decl.getImage())) {
37                  continue;
38              }
39              if (!actuallyUsed(entry.getValue())) {
40                  if (!usedInOuterClass(node, decl) && !usedInOuterEnum(node, decl)) {
41                      addViolation(data, decl.getNode(), decl.getImage());
42                  }
43              }
44          }
45          return super.visit(node, data);
46      }
47  
48      private boolean usedInOuterEnum(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) {
49          List<ASTEnumDeclaration> outerEnums = node.getParentsOfType(ASTEnumDeclaration.class);
50          for (ASTEnumDeclaration outerEnum : outerEnums) {
51              ASTEnumBody enumBody = outerEnum.getFirstChildOfType(ASTEnumBody.class);
52              if (usedInOuter(decl, enumBody)) {
53                  return true;
54              }
55          }
56          return false;
57      }
58  
59      /**
60       * Find out whether the variable is used in an outer class
61       */
62      private boolean usedInOuterClass(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) {
63          List<ASTClassOrInterfaceDeclaration> outerClasses = node.getParentsOfType(ASTClassOrInterfaceDeclaration.class);
64          for (ASTClassOrInterfaceDeclaration outerClass : outerClasses) {
65              ASTClassOrInterfaceBody classOrInterfaceBody = outerClass.getFirstChildOfType(ASTClassOrInterfaceBody.class);
66              if (usedInOuter(decl, classOrInterfaceBody)) {
67                  return true;
68              }
69          }
70          return false;
71      }
72  
73      private boolean usedInOuter(NameDeclaration decl, JavaNode body) {
74          List<ASTClassOrInterfaceBodyDeclaration> classOrInterfaceBodyDeclarations = body
75                  .findChildrenOfType(ASTClassOrInterfaceBodyDeclaration.class);
76          List<ASTEnumConstant> enumConstants = body
77                  .findChildrenOfType(ASTEnumConstant.class);
78          List<JavaNode> nodes = new ArrayList<JavaNode>();
79          nodes.addAll(classOrInterfaceBodyDeclarations);
80          nodes.addAll(enumConstants);
81  
82          for (JavaNode node : nodes) {
83              List<ASTPrimarySuffix> primarySuffixes = node.findDescendantsOfType(ASTPrimarySuffix.class);
84              for (ASTPrimarySuffix primarySuffix : primarySuffixes) {
85                  if (decl.getImage().equals(primarySuffix.getImage())) {
86                      return true; // No violation
87                  }
88              }
89  
90              List<ASTPrimaryPrefix> primaryPrefixes = node.findDescendantsOfType(ASTPrimaryPrefix.class);
91              for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) {
92                  ASTName name = primaryPrefix.getFirstDescendantOfType(ASTName.class);
93  
94                  if (name != null) {
95                      for (String id : name.getImage().split("\\.")) {
96                          if (id.equals(decl.getImage())) {
97                              return true; // No violation
98                          }
99                      }
100                 }
101             }
102         }
103         return false;
104     }
105 
106     private boolean actuallyUsed(List<NameOccurrence> usages) {
107         for (NameOccurrence nameOccurrence : usages) {
108             JavaNameOccurrence jNameOccurrence = (JavaNameOccurrence) nameOccurrence;
109             if (!jNameOccurrence.isOnLeftHandSide()) {
110                 return true;
111             }
112         }
113 
114         return false;
115     }
116 
117     private boolean isOK(String image) {
118         return image.equals("serialVersionUID") || image.equals("serialPersistentFields") || image.equals("IDENT");
119     }
120 }