1
2
3
4 package net.sourceforge.pmd.lang.java.rule.design;
5
6 import net.sourceforge.pmd.lang.ast.Node;
7 import net.sourceforge.pmd.lang.java.ast.ASTBlock;
8 import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
9 import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
10 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
11 import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
12 import net.sourceforge.pmd.lang.java.ast.ASTResultType;
13 import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
14 import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus;
15 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
16
17 public class SimplifyBooleanReturnsRule extends AbstractJavaRule {
18
19 public Object visit(ASTMethodDeclaration node, Object data) {
20
21 ASTResultType r = node.getResultType();
22
23 if (!r.isVoid()) {
24 Node t = r.jjtGetChild(0);
25 if (t.jjtGetNumChildren() == 1) {
26 t = t.jjtGetChild(0);
27 if (t instanceof ASTPrimitiveType && ((ASTPrimitiveType) t).isBoolean()) {
28 return super.visit(node, data);
29 }
30 }
31 }
32
33 return data;
34 }
35
36 public Object visit(ASTIfStatement node, Object data) {
37
38 if (!node.hasElse() && isIfJustReturnsBoolean(node) && isJustReturnsBooleanAfter(node)) {
39 addViolation(data, node);
40 return super.visit(node, data);
41 }
42
43
44 if (node.jjtGetNumChildren() != 3) {
45 return super.visit(node, data);
46 }
47
48
49 if (node.jjtGetChild(1).jjtGetNumChildren() == 0 || node.jjtGetChild(2).jjtGetNumChildren() == 0) {
50 return super.visit(node, data);
51 }
52
53 Node returnStatement1 = node.jjtGetChild(1).jjtGetChild(0);
54 Node returnStatement2 = node.jjtGetChild(2).jjtGetChild(0);
55
56 if (returnStatement1 instanceof ASTReturnStatement && returnStatement2 instanceof ASTReturnStatement) {
57 Node expression1 = returnStatement1.jjtGetChild(0).jjtGetChild(0);
58 Node expression2 = returnStatement2.jjtGetChild(0).jjtGetChild(0);
59 if (terminatesInBooleanLiteral(returnStatement1) && terminatesInBooleanLiteral(returnStatement2)) {
60 addViolation(data, node);
61 } else if (expression1 instanceof ASTUnaryExpressionNotPlusMinus
62 ^ expression2 instanceof ASTUnaryExpressionNotPlusMinus) {
63
64
65 if (isNodesEqualWithUnaryExpression(expression1, expression2)) {
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 addViolation(data, node);
82 }
83 }
84 } else if (hasOneBlockStmt(node.jjtGetChild(1)) && hasOneBlockStmt(node.jjtGetChild(2))) {
85
86
87 returnStatement1 = returnStatement1.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
88 returnStatement2 = returnStatement2.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
89
90
91 if (isSimpleReturn(returnStatement1) && isSimpleReturn(returnStatement2)) {
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 addViolation(data, node);
112 } else {
113 Node expression1 = getDescendant(returnStatement1, 4);
114 Node expression2 = getDescendant(returnStatement2, 4);
115 if (terminatesInBooleanLiteral(node.jjtGetChild(1).jjtGetChild(0))
116 && terminatesInBooleanLiteral(node.jjtGetChild(2).jjtGetChild(0))) {
117 addViolation(data, node);
118 } else if (expression1 instanceof ASTUnaryExpressionNotPlusMinus
119 ^ expression2 instanceof ASTUnaryExpressionNotPlusMinus) {
120
121
122 if (isNodesEqualWithUnaryExpression(expression1, expression2)) {
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 addViolation(data, node);
146 }
147 }
148 }
149 }
150 return super.visit(node, data);
151 }
152
153
154
155
156
157
158
159
160
161 private boolean isJustReturnsBooleanAfter(ASTIfStatement ifNode) {
162 Node blockStatement = ifNode.jjtGetParent().jjtGetParent();
163 Node block = blockStatement.jjtGetParent();
164 if (block.jjtGetNumChildren() != blockStatement.jjtGetChildIndex() + 1 + 1) {
165 return false;
166 }
167
168 Node nextBlockStatement = block.jjtGetChild(blockStatement.jjtGetChildIndex() + 1);
169 return terminatesInBooleanLiteral(nextBlockStatement);
170 }
171
172
173
174
175
176
177
178
179
180 private boolean isIfJustReturnsBoolean(ASTIfStatement ifNode) {
181 Node node = ifNode.jjtGetChild(1);
182 return node.jjtGetNumChildren() == 1
183 && (hasOneBlockStmt(node) || terminatesInBooleanLiteral(node.jjtGetChild(0)));
184 }
185
186 private boolean hasOneBlockStmt(Node node) {
187 return node.jjtGetChild(0) instanceof ASTBlock && node.jjtGetChild(0).jjtGetNumChildren() == 1
188 && terminatesInBooleanLiteral(node.jjtGetChild(0).jjtGetChild(0));
189 }
190
191
192
193
194
195 private Node getDescendant(Node node, int level) {
196 Node n = node;
197 for (int i = 0; i < level; i++) {
198 if (n.jjtGetNumChildren() == 0) {
199 return null;
200 }
201 n = n.jjtGetChild(0);
202 }
203 return n;
204 }
205
206 private boolean terminatesInBooleanLiteral(Node node) {
207 return eachNodeHasOneChild(node) && getLastChild(node) instanceof ASTBooleanLiteral;
208 }
209
210 private boolean eachNodeHasOneChild(Node node) {
211 if (node.jjtGetNumChildren() > 1) {
212 return false;
213 }
214 if (node.jjtGetNumChildren() == 0) {
215 return true;
216 }
217 return eachNodeHasOneChild(node.jjtGetChild(0));
218 }
219
220 private Node getLastChild(Node node) {
221 if (node.jjtGetNumChildren() == 0) {
222 return node;
223 }
224 return getLastChild(node.jjtGetChild(0));
225 }
226
227 private boolean isNodesEqualWithUnaryExpression(Node n1, Node n2) {
228 Node node1;
229 Node node2;
230 if (n1 instanceof ASTUnaryExpressionNotPlusMinus) {
231 node1 = n1.jjtGetChild(0);
232 } else {
233 node1 = n1;
234 }
235 if (n2 instanceof ASTUnaryExpressionNotPlusMinus) {
236 node2 = n2.jjtGetChild(0);
237 } else {
238 node2 = n2;
239 }
240 return isNodesEquals(node1, node2);
241 }
242
243 private boolean isNodesEquals(Node n1, Node n2) {
244 int numberChild1 = n1.jjtGetNumChildren();
245 int numberChild2 = n2.jjtGetNumChildren();
246 if (numberChild1 != numberChild2) {
247 return false;
248 }
249 if (!n1.getClass().equals(n2.getClass())) {
250 return false;
251 }
252 if (!n1.toString().equals(n2.toString())) {
253 return false;
254 }
255 for (int i = 0; i < numberChild1; i++) {
256 if (!isNodesEquals(n1.jjtGetChild(i), n2.jjtGetChild(i))) {
257 return false;
258 }
259 }
260 return true;
261 }
262
263 private boolean isSimpleReturn(Node node) {
264 return node instanceof ASTReturnStatement && node.jjtGetNumChildren() == 0;
265 }
266
267 }