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