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