1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules.design;
5
6 import net.sourceforge.pmd.AbstractRule;
7 import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
8 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
9 import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
10 import net.sourceforge.pmd.ast.ASTDoStatement;
11 import net.sourceforge.pmd.ast.ASTForStatement;
12 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
13 import net.sourceforge.pmd.ast.ASTTryStatement;
14 import net.sourceforge.pmd.ast.ASTVariableInitializer;
15 import net.sourceforge.pmd.ast.ASTWhileStatement;
16 import net.sourceforge.pmd.ast.SimpleNode;
17 import net.sourceforge.pmd.symboltable.NameOccurrence;
18 import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
19
20 import java.util.ArrayList;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 /***
28 * @author Olander
29 */
30 public class ImmutableField extends AbstractRule {
31
32 private static final int MUTABLE = 0;
33 private static final int IMMUTABLE = 1;
34 private static final int CHECKDECL = 2;
35
36 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
37 Map vars = node.getScope().getVariableDeclarations();
38 List constructors = findAllConstructors(node);
39 for (Iterator i = vars.entrySet().iterator(); i.hasNext();) {
40 Map.Entry entry = (Map.Entry) i.next();
41 VariableNameDeclaration field = (VariableNameDeclaration) entry.getKey();
42 if (field.getAccessNodeParent().isStatic() || !field.getAccessNodeParent().isPrivate() || field.getAccessNodeParent().isFinal()) {
43 continue;
44 }
45
46 int result = initializedInConstructor((List) entry.getValue(), new HashSet(constructors));
47 if (result == MUTABLE) {
48 continue;
49 }
50 if (result == IMMUTABLE || (result == CHECKDECL && initializedWhenDeclared(field))) {
51 addViolation(data, field.getNode(), field.getImage());
52 }
53 }
54 return super.visit(node, data);
55 }
56
57 private boolean initializedWhenDeclared(VariableNameDeclaration field) {
58 return !field.getAccessNodeParent().findChildrenOfType(ASTVariableInitializer.class).isEmpty();
59 }
60
61 private int initializedInConstructor(List usages, Set allConstructors) {
62 int result = MUTABLE, methodInitCount = 0;
63 Set consSet = new HashSet();
64 for (Iterator j = usages.iterator(); j.hasNext();) {
65 NameOccurrence occ = (NameOccurrence) j.next();
66 if (occ.isOnLeftHandSide() || occ.isSelfAssignment()) {
67 SimpleNode node = occ.getLocation();
68 SimpleNode constructor = (SimpleNode) node.getFirstParentOfType(ASTConstructorDeclaration.class);
69 if (constructor != null) {
70 if (inLoopOrTry(node)) {
71 continue;
72 }
73 if (inAnonymousInnerClass(node)) {
74 methodInitCount++;
75 } else {
76 consSet.add(constructor);
77 }
78 } else {
79 if (node.getFirstParentOfType(ASTMethodDeclaration.class) != null) {
80 methodInitCount++;
81 }
82 }
83 }
84 }
85 if (usages.isEmpty() || ((methodInitCount == 0) && consSet.isEmpty())) {
86 result = CHECKDECL;
87 } else {
88 allConstructors.removeAll(consSet);
89 if (allConstructors.isEmpty() && (methodInitCount == 0)) {
90 result = IMMUTABLE;
91 }
92 }
93 return result;
94 }
95
96 private boolean inLoopOrTry(SimpleNode node) {
97 return (SimpleNode) node.getFirstParentOfType(ASTTryStatement.class) != null ||
98 (SimpleNode) node.getFirstParentOfType(ASTForStatement.class) != null ||
99 (SimpleNode) node.getFirstParentOfType(ASTWhileStatement.class) != null ||
100 (SimpleNode) node.getFirstParentOfType(ASTDoStatement.class) != null;
101 }
102
103 private boolean inAnonymousInnerClass(SimpleNode node) {
104 ASTClassOrInterfaceBodyDeclaration parent = (ASTClassOrInterfaceBodyDeclaration) node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class);
105 return parent != null && parent.isAnonymousInnerClass();
106 }
107
108 private List findAllConstructors(ASTClassOrInterfaceDeclaration node) {
109 List cons = new ArrayList();
110 node.findChildrenOfType(ASTConstructorDeclaration.class, cons, false);
111 return cons;
112 }
113 }