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              for (int i = 0; i < node.jjtGetNumChildren(); i++) {
84                  if (node.jjtGetChild(i) instanceof ASTClassOrInterfaceDeclaration) {
85                      continue; // Skip other inner classes
86                  }
87  
88                  List<ASTPrimarySuffix> primarySuffixes = node
89                          .findDescendantsOfType(ASTPrimarySuffix.class);
90                  for (ASTPrimarySuffix primarySuffix : primarySuffixes) {
91                      if (decl.getImage().equals(primarySuffix.getImage())) {
92                          return true; // No violation
93                      }
94                  }
95  
96                  List<ASTPrimaryPrefix> primaryPrefixes = node
97                          .findDescendantsOfType(ASTPrimaryPrefix.class);
98                  for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) {
99                      ASTName name = primaryPrefix.getFirstDescendantOfType(ASTName.class);
100 
101                     if (name != null && name.getImage().endsWith(decl.getImage())) {
102                         return true; // No violation
103                     }
104                 }
105             }
106         }
107         return false;
108     }
109 
110     private boolean actuallyUsed(List<NameOccurrence> usages) {
111         for (NameOccurrence nameOccurrence : usages) {
112             JavaNameOccurrence jNameOccurrence = (JavaNameOccurrence) nameOccurrence;
113             if (!jNameOccurrence.isOnLeftHandSide()) {
114                 return true;
115             }
116         }
117 
118         return false;
119     }
120 
121     private boolean isOK(String image) {
122         return image.equals("serialVersionUID") || image.equals("serialPersistentFields") || image.equals("IDENT");
123     }
124 }