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