1
2
3
4
5
6 package net.sourceforge.pmd.rules.design;
7
8 import java.util.ArrayList;
9 import java.util.List;
10
11 import net.sourceforge.pmd.AbstractRule;
12 import net.sourceforge.pmd.PropertyDescriptor;
13 import net.sourceforge.pmd.ast.ASTAssignmentOperator;
14 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
15 import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
16 import net.sourceforge.pmd.ast.ASTFieldDeclaration;
17 import net.sourceforge.pmd.ast.ASTIfStatement;
18 import net.sourceforge.pmd.ast.ASTInitializer;
19 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
20 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
21 import net.sourceforge.pmd.ast.ASTStatementExpression;
22 import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
23 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
24 import net.sourceforge.pmd.ast.Node;
25 import net.sourceforge.pmd.ast.SimpleNode;
26 import net.sourceforge.pmd.properties.BooleanProperty;
27 import net.sourceforge.pmd.symboltable.NameOccurrence;
28
29 /**
30 * @author Eric Olander
31 * @author Wouter Zelle
32 */
33 public class SingularField extends AbstractRule {
34
35 /**
36 * Restore old behaviour by setting both properties to true, which will result in many false positives
37 */
38 private static final PropertyDescriptor CHECK_INNER_CLASSES = new BooleanProperty(
39 "CheckInnerClasses", "Check inner classes", false, 1.0f);
40 private static final PropertyDescriptor DISALLOW_NOT_ASSIGNMENT = new BooleanProperty(
41 "DisallowNotAssignment", "Disallow violations where the first usage is not an assignment", false, 1.0f);
42
43 public Object visit(ASTFieldDeclaration node, Object data) {
44 boolean checkInnerClasses = getBooleanProperty(CHECK_INNER_CLASSES);
45 boolean disallowNotAssignment = getBooleanProperty(DISALLOW_NOT_ASSIGNMENT);
46
47 if (node.isPrivate() && !node.isStatic()) {
48 List<ASTVariableDeclaratorId> list = node.findChildrenOfType(ASTVariableDeclaratorId.class);
49 ASTVariableDeclaratorId declaration = list.get(0);
50 List<NameOccurrence> usages = declaration.getUsages();
51 SimpleNode decl = null;
52 boolean violation = true;
53 for (int ix = 0; ix < usages.size(); ix++) {
54 NameOccurrence no = usages.get(ix);
55 SimpleNode location = no.getLocation();
56
57 ASTPrimaryExpression primaryExpressionParent = location.getFirstParentOfType(ASTPrimaryExpression.class);
58 if (ix==0 && !disallowNotAssignment) {
59 if (primaryExpressionParent.getFirstParentOfType(ASTIfStatement.class) != null) {
60
61
62
63 violation = false;
64 break;
65 }
66
67
68 Node potentialStatement = primaryExpressionParent.jjtGetParent();
69 boolean assignmentToField = no.getImage().equals(location.getImage());
70 if (!assignmentToField || !isInAssignment(potentialStatement)) {
71 violation = false;
72 break;
73 } else {
74 if (usages.size() > ix + 1) {
75 SimpleNode secondUsageLocation = usages.get(ix + 1).getLocation();
76
77 List<ASTStatementExpression> parentStatements = secondUsageLocation.getParentsOfType(ASTStatementExpression.class);
78 for (ASTStatementExpression statementExpression : parentStatements) {
79 if (statementExpression != null && statementExpression.equals(potentialStatement)) {
80
81 violation = false;
82 break;
83 }
84 }
85
86 }
87 }
88 }
89
90 if (!checkInnerClasses) {
91
92 ASTClassOrInterfaceDeclaration clazz = location.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
93 if (clazz!= null && clazz.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class) != null) {
94 violation = false;
95 break;
96 }
97 }
98
99 if (primaryExpressionParent.jjtGetParent() instanceof ASTSynchronizedStatement) {
100
101 violation = false;
102 break;
103 }
104
105 SimpleNode method = location.getFirstParentOfType(ASTMethodDeclaration.class);
106 if (method == null) {
107 method = location.getFirstParentOfType(ASTConstructorDeclaration.class);
108 if (method == null) {
109 method = location.getFirstParentOfType(ASTInitializer.class);
110 if (method == null) {
111 continue;
112 }
113 }
114 }
115
116 if (decl == null) {
117 decl = method;
118 continue;
119 } else if (decl != method) {
120 violation = false;
121 break;
122 }
123
124
125 }
126
127 if (violation && !usages.isEmpty()) {
128 addViolation(data, node, new Object[] { declaration.getImage() });
129 }
130 }
131 return data;
132 }
133
134 private boolean isInAssignment(Node potentialStatement) {
135 if (potentialStatement instanceof ASTStatementExpression) {
136 ASTStatementExpression statement = (ASTStatementExpression)potentialStatement;
137 List<ASTAssignmentOperator> assignments = new ArrayList<ASTAssignmentOperator>();
138 statement.findChildrenOfType(ASTAssignmentOperator.class, assignments, false);
139 if (assignments.isEmpty() || !"=".equals(assignments.get(0).getImage())) {
140 return false;
141 } else {
142 return true;
143 }
144 } else {
145 return false;
146 }
147 }
148 }