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 @StatelessCheck
50 public class EmptyBlockCheck
51 extends AbstractCheck {
52
53
54
55
56
57 public static final String MSG_KEY_BLOCK_NO_STATEMENT = "block.noStatement";
58
59
60
61
62
63 public static final String MSG_KEY_BLOCK_EMPTY = "block.empty";
64
65
66 private BlockOption option = BlockOption.STATEMENT;
67
68
69
70
71 public EmptyBlockCheck() {
72
73 }
74
75
76
77
78
79
80
81
82 public void setOption(String optionStr) {
83 option = BlockOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
84 }
85
86 @Override
87 public int[] getDefaultTokens() {
88 return new int[] {
89 TokenTypes.LITERAL_WHILE,
90 TokenTypes.LITERAL_TRY,
91 TokenTypes.LITERAL_FINALLY,
92 TokenTypes.LITERAL_DO,
93 TokenTypes.LITERAL_IF,
94 TokenTypes.LITERAL_ELSE,
95 TokenTypes.LITERAL_FOR,
96 TokenTypes.INSTANCE_INIT,
97 TokenTypes.STATIC_INIT,
98 TokenTypes.LITERAL_SWITCH,
99 TokenTypes.LITERAL_SYNCHRONIZED,
100 };
101 }
102
103 @Override
104 public int[] getAcceptableTokens() {
105 return new int[] {
106 TokenTypes.LITERAL_WHILE,
107 TokenTypes.LITERAL_TRY,
108 TokenTypes.LITERAL_CATCH,
109 TokenTypes.LITERAL_FINALLY,
110 TokenTypes.LITERAL_DO,
111 TokenTypes.LITERAL_IF,
112 TokenTypes.LITERAL_ELSE,
113 TokenTypes.LITERAL_FOR,
114 TokenTypes.INSTANCE_INIT,
115 TokenTypes.STATIC_INIT,
116 TokenTypes.LITERAL_SWITCH,
117 TokenTypes.LITERAL_SYNCHRONIZED,
118 TokenTypes.LITERAL_CASE,
119 TokenTypes.LITERAL_DEFAULT,
120 TokenTypes.ARRAY_INIT,
121 };
122 }
123
124 @Override
125 public int[] getRequiredTokens() {
126 return CommonUtil.EMPTY_INT_ARRAY;
127 }
128
129 @Override
130 public void visitToken(DetailAST ast) {
131 final Optional<DetailAST> leftCurly = getLeftCurly(ast);
132 if (leftCurly.isPresent()) {
133 final DetailAST leftCurlyAST = leftCurly.orElseThrow();
134 if (option == BlockOption.STATEMENT) {
135 final boolean emptyBlock;
136 if (leftCurlyAST.getType() == TokenTypes.LCURLY) {
137 final DetailAST nextSibling = leftCurlyAST.getNextSibling();
138 emptyBlock = nextSibling.getType() != TokenTypes.CASE_GROUP
139 && nextSibling.getType() != TokenTypes.SWITCH_RULE;
140 }
141 else {
142 emptyBlock = leftCurlyAST.getChildCount() <= 1;
143 }
144 if (emptyBlock) {
145 log(leftCurlyAST,
146 MSG_KEY_BLOCK_NO_STATEMENT);
147 }
148 }
149 else if (!hasText(leftCurlyAST)) {
150 log(leftCurlyAST,
151 MSG_KEY_BLOCK_EMPTY,
152 ast.getText());
153 }
154 }
155 }
156
157
158
159
160
161
162
163 private boolean hasText(final DetailAST slistAST) {
164 final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY);
165 final DetailAST rcurlyAST;
166
167 if (rightCurly == null) {
168 rcurlyAST = slistAST.getParent().findFirstToken(TokenTypes.RCURLY);
169 }
170 else {
171 rcurlyAST = rightCurly;
172 }
173 final int slistLineNo = slistAST.getLineNo();
174 final int slistColNo = slistAST.getColumnNo();
175 final int rcurlyLineNo = rcurlyAST.getLineNo();
176 final int rcurlyColNo = rcurlyAST.getColumnNo();
177 boolean returnValue = false;
178 if (slistLineNo == rcurlyLineNo) {
179
180 final int[] txt = Arrays.copyOfRange(getLineCodePoints(slistLineNo - 1),
181 slistColNo + 1, rcurlyColNo);
182
183 if (!CodePointUtil.isBlank(txt)) {
184 returnValue = true;
185 }
186 }
187 else {
188 final int[] codePointsFirstLine = getLineCodePoints(slistLineNo - 1);
189 final int[] firstLine = Arrays.copyOfRange(codePointsFirstLine,
190 slistColNo + 1, codePointsFirstLine.length);
191 final int[] codePointsLastLine = getLineCodePoints(rcurlyLineNo - 1);
192 final int[] lastLine = Arrays.copyOfRange(codePointsLastLine, 0, rcurlyColNo);
193
194 returnValue = !(CodePointUtil.isBlank(firstLine) && CodePointUtil.isBlank(lastLine))
195 || !checkIsAllLinesAreWhitespace(slistLineNo, rcurlyLineNo);
196 }
197 return returnValue;
198 }
199
200
201
202
203
204
205
206
207
208
209
210 private boolean checkIsAllLinesAreWhitespace(int lineFrom, int lineTo) {
211 boolean result = true;
212 for (int i = lineFrom; i < lineTo - 1; i++) {
213 if (!CodePointUtil.isBlank(getLineCodePoints(i))) {
214 result = false;
215 break;
216 }
217 }
218 return result;
219 }
220
221
222
223
224
225
226
227 private static Optional<DetailAST> getLeftCurly(DetailAST ast) {
228 final DetailAST parent = ast.getParent();
229 final int parentType = parent.getType();
230 final Optional<DetailAST> leftCurly;
231
232 if (parentType == TokenTypes.SWITCH_RULE) {
233
234 leftCurly = Optional.ofNullable(parent.findFirstToken(TokenTypes.SLIST));
235 }
236 else if (parentType == TokenTypes.CASE_GROUP) {
237
238 leftCurly = Optional.ofNullable(ast.getNextSibling())
239 .map(DetailAST::getFirstChild)
240 .filter(node -> node.getType() == TokenTypes.SLIST);
241 }
242 else if (ast.findFirstToken(TokenTypes.SLIST) != null) {
243
244 leftCurly = Optional.of(ast.findFirstToken(TokenTypes.SLIST));
245 }
246 else {
247
248 leftCurly = Optional.ofNullable(ast.findFirstToken(TokenTypes.LCURLY));
249 }
250 return leftCurly;
251 }
252
253 }