1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules;
5
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Map;
9
10 import net.sourceforge.pmd.AbstractRule;
11 import net.sourceforge.pmd.ast.ASTClassOrInterfaceBody;
12 import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
13 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
14 import net.sourceforge.pmd.ast.ASTName;
15 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
16 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
17 import net.sourceforge.pmd.symboltable.NameOccurrence;
18 import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
19
20 public class UnusedPrivateFieldRule extends AbstractRule {
21
22 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
23 Map<VariableNameDeclaration, List<NameOccurrence>> vars = node.getScope().getVariableDeclarations();
24 for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry: vars.entrySet()) {
25 VariableNameDeclaration decl = entry.getKey();
26 if (!decl.getAccessNodeParent().isPrivate() || isOK(decl.getImage())) {
27 continue;
28 }
29 if (!actuallyUsed(entry.getValue())) {
30 if (!usedInOuterClass(node, decl)) {
31 addViolation(data, decl.getNode(), decl.getImage());
32 }
33 }
34 }
35 return super.visit(node, data);
36 }
37
38 /**
39 * Find out whether the variable is used in an outer class
40 */
41 private boolean usedInOuterClass(ASTClassOrInterfaceDeclaration node,
42 VariableNameDeclaration decl) {
43 List<ASTClassOrInterfaceDeclaration> outerClasses = node.getParentsOfType(ASTClassOrInterfaceDeclaration.class);
44 for (ASTClassOrInterfaceDeclaration outerClass : outerClasses) {
45 ASTClassOrInterfaceBody classOrInterfaceBody = outerClass.getFirstChildOfType(ASTClassOrInterfaceBody.class);
46
47 List<ASTClassOrInterfaceBodyDeclaration> classOrInterfaceBodyDeclarations = new ArrayList<ASTClassOrInterfaceBodyDeclaration>();
48 classOrInterfaceBody.findChildrenOfType(ASTClassOrInterfaceBodyDeclaration.class, classOrInterfaceBodyDeclarations, false);
49
50 for (ASTClassOrInterfaceBodyDeclaration classOrInterfaceBodyDeclaration : classOrInterfaceBodyDeclarations) {
51 for (int i = 0; i < classOrInterfaceBodyDeclaration.jjtGetNumChildren(); i++) {
52 if (classOrInterfaceBodyDeclaration.jjtGetChild(i) instanceof ASTClassOrInterfaceDeclaration) {
53 continue;
54 }
55
56 List<ASTPrimarySuffix> primarySuffixes = classOrInterfaceBodyDeclaration.findChildrenOfType(ASTPrimarySuffix.class);
57 for (ASTPrimarySuffix primarySuffix : primarySuffixes) {
58 if (decl.getImage().equals(primarySuffix.getImage())) {
59 return true;
60 }
61 }
62
63 List<ASTPrimaryPrefix> primaryPrefixes = classOrInterfaceBodyDeclaration.findChildrenOfType(ASTPrimaryPrefix.class);
64 for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) {
65 ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class);
66
67 if (name != null && name.getImage().endsWith(decl.getImage())) {
68 return true;
69 }
70 }
71 }
72 }
73
74 }
75
76 return false;
77 }
78
79 private boolean actuallyUsed(List<NameOccurrence> usages) {
80 for (NameOccurrence nameOccurrence: usages) {
81 if (!nameOccurrence.isOnLeftHandSide()) {
82 return true;
83 }
84 }
85
86 return false;
87 }
88
89 private boolean isOK(String image) {
90 return image.equals("serialVersionUID") || image.equals("serialPersistentFields") || image.equals("IDENT");
91 }
92 }