1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.blocks;
21
22 import java.util.Arrays;
23 import java.util.Locale;
24 import java.util.Optional;
25
26 import com.puppycrawl.tools.checkstyle.StatelessCheck;
27 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28 import com.puppycrawl.tools.checkstyle.api.DetailAST;
29 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30 import com.puppycrawl.tools.checkstyle.utils.CodePointUtil;
31 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 @StatelessCheck
101 public class EmptyBlockCheck
102 extends AbstractCheck {
103
104
105
106
107
108 public static final String MSG_KEY_BLOCK_NO_STATEMENT = "block.noStatement";
109
110
111
112
113
114 public static final String MSG_KEY_BLOCK_EMPTY = "block.empty";
115
116
117 private BlockOption option = BlockOption.STATEMENT;
118
119
120
121
122
123
124
125
126 public void setOption(String optionStr) {
127 option = BlockOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
128 }
129
130 @Override
131 public int[] getDefaultTokens() {
132 return new int[] {
133 TokenTypes.LITERAL_WHILE,
134 TokenTypes.LITERAL_TRY,
135 TokenTypes.LITERAL_FINALLY,
136 TokenTypes.LITERAL_DO,
137 TokenTypes.LITERAL_IF,
138 TokenTypes.LITERAL_ELSE,
139 TokenTypes.LITERAL_FOR,
140 TokenTypes.INSTANCE_INIT,
141 TokenTypes.STATIC_INIT,
142 TokenTypes.LITERAL_SWITCH,
143 TokenTypes.LITERAL_SYNCHRONIZED,
144 };
145 }
146
147 @Override
148 public int[] getAcceptableTokens() {
149 return new int[] {
150 TokenTypes.LITERAL_WHILE,
151 TokenTypes.LITERAL_TRY,
152 TokenTypes.LITERAL_CATCH,
153 TokenTypes.LITERAL_FINALLY,
154 TokenTypes.LITERAL_DO,
155 TokenTypes.LITERAL_IF,
156 TokenTypes.LITERAL_ELSE,
157 TokenTypes.LITERAL_FOR,
158 TokenTypes.INSTANCE_INIT,
159 TokenTypes.STATIC_INIT,
160 TokenTypes.LITERAL_SWITCH,
161 TokenTypes.LITERAL_SYNCHRONIZED,
162 TokenTypes.LITERAL_CASE,
163 TokenTypes.LITERAL_DEFAULT,
164 TokenTypes.ARRAY_INIT,
165 };
166 }
167
168 @Override
169 public int[] getRequiredTokens() {
170 return CommonUtil.EMPTY_INT_ARRAY;
171 }
172
173 @Override
174 public void visitToken(DetailAST ast) {
175 final Optional<DetailAST> leftCurly = getLeftCurly(ast);
176 if (leftCurly.isPresent()) {
177 final DetailAST leftCurlyAST = leftCurly.orElseThrow();
178 if (option == BlockOption.STATEMENT) {
179 final boolean emptyBlock;
180 if (leftCurlyAST.getType() == TokenTypes.LCURLY) {
181 final DetailAST nextSibling = leftCurlyAST.getNextSibling();
182 emptyBlock = nextSibling.getType() != TokenTypes.CASE_GROUP
183 && nextSibling.getType() != TokenTypes.SWITCH_RULE;
184 }
185 else {
186 emptyBlock = leftCurlyAST.getChildCount() <= 1;
187 }
188 if (emptyBlock) {
189 log(leftCurlyAST,
190 MSG_KEY_BLOCK_NO_STATEMENT);
191 }
192 }
193 else if (!hasText(leftCurlyAST)) {
194 log(leftCurlyAST,
195 MSG_KEY_BLOCK_EMPTY,
196 ast.getText());
197 }
198 }
199 }
200
201
202
203
204
205
206
207 private boolean hasText(final DetailAST slistAST) {
208 final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY);
209 final DetailAST rcurlyAST;
210
211 if (rightCurly == null) {
212 rcurlyAST = slistAST.getParent().findFirstToken(TokenTypes.RCURLY);
213 }
214 else {
215 rcurlyAST = rightCurly;
216 }
217 final int slistLineNo = slistAST.getLineNo();
218 final int slistColNo = slistAST.getColumnNo();
219 final int rcurlyLineNo = rcurlyAST.getLineNo();
220 final int rcurlyColNo = rcurlyAST.getColumnNo();
221 boolean returnValue = false;
222 if (slistLineNo == rcurlyLineNo) {
223
224 final int[] txt = Arrays.copyOfRange(getLineCodePoints(slistLineNo - 1),
225 slistColNo + 1, rcurlyColNo);
226
227 if (!CodePointUtil.isBlank(txt)) {
228 returnValue = true;
229 }
230 }
231 else {
232 final int[] codePointsFirstLine = getLineCodePoints(slistLineNo - 1);
233 final int[] firstLine = Arrays.copyOfRange(codePointsFirstLine,
234 slistColNo + 1, codePointsFirstLine.length);
235 final int[] codePointsLastLine = getLineCodePoints(rcurlyLineNo - 1);
236 final int[] lastLine = Arrays.copyOfRange(codePointsLastLine, 0, rcurlyColNo);
237
238 returnValue = !(CodePointUtil.isBlank(firstLine) && CodePointUtil.isBlank(lastLine))
239 || !checkIsAllLinesAreWhitespace(slistLineNo, rcurlyLineNo);
240 }
241 return returnValue;
242 }
243
244
245
246
247
248
249
250
251
252
253
254 private boolean checkIsAllLinesAreWhitespace(int lineFrom, int lineTo) {
255 boolean result = true;
256 for (int i = lineFrom; i < lineTo - 1; i++) {
257 if (!CodePointUtil.isBlank(getLineCodePoints(i))) {
258 result = false;
259 break;
260 }
261 }
262 return result;
263 }
264
265
266
267
268
269
270
271 private static Optional<DetailAST> getLeftCurly(DetailAST ast) {
272 final DetailAST parent = ast.getParent();
273 final int parentType = parent.getType();
274 final Optional<DetailAST> leftCurly;
275
276 if (parentType == TokenTypes.SWITCH_RULE) {
277
278 leftCurly = Optional.ofNullable(parent.findFirstToken(TokenTypes.SLIST));
279 }
280 else if (parentType == TokenTypes.CASE_GROUP) {
281
282 leftCurly = Optional.ofNullable(ast.getNextSibling())
283 .map(DetailAST::getFirstChild)
284 .filter(node -> node.getType() == TokenTypes.SLIST);
285 }
286 else if (ast.findFirstToken(TokenTypes.SLIST) != null) {
287
288 leftCurly = Optional.of(ast.findFirstToken(TokenTypes.SLIST));
289 }
290 else {
291
292 leftCurly = Optional.ofNullable(ast.findFirstToken(TokenTypes.LCURLY));
293 }
294 return leftCurly;
295 }
296
297 }