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