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.Locale;
23
24 import javax.annotation.Nullable;
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.CommonUtil;
31 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 @StatelessCheck
128 public class LeftCurlyCheck
129 extends AbstractCheck {
130
131
132
133
134
135 public static final String MSG_KEY_LINE_NEW = "line.new";
136
137
138
139
140
141 public static final String MSG_KEY_LINE_PREVIOUS = "line.previous";
142
143
144
145
146
147 public static final String MSG_KEY_LINE_BREAK_AFTER = "line.break.after";
148
149
150 private static final String OPEN_CURLY_BRACE = "{";
151
152
153 private boolean ignoreEnums = true;
154
155
156
157
158 private LeftCurlyOption option = LeftCurlyOption.EOL;
159
160
161
162
163
164
165
166
167 public void setOption(String optionStr) {
168 option = LeftCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
169 }
170
171
172
173
174
175
176
177 public void setIgnoreEnums(boolean ignoreEnums) {
178 this.ignoreEnums = ignoreEnums;
179 }
180
181 @Override
182 public int[] getDefaultTokens() {
183 return getAcceptableTokens();
184 }
185
186 @Override
187 public int[] getAcceptableTokens() {
188 return new int[] {
189 TokenTypes.ANNOTATION_DEF,
190 TokenTypes.CLASS_DEF,
191 TokenTypes.CTOR_DEF,
192 TokenTypes.ENUM_CONSTANT_DEF,
193 TokenTypes.ENUM_DEF,
194 TokenTypes.INTERFACE_DEF,
195 TokenTypes.LAMBDA,
196 TokenTypes.LITERAL_CASE,
197 TokenTypes.LITERAL_CATCH,
198 TokenTypes.LITERAL_DEFAULT,
199 TokenTypes.LITERAL_DO,
200 TokenTypes.LITERAL_ELSE,
201 TokenTypes.LITERAL_FINALLY,
202 TokenTypes.LITERAL_FOR,
203 TokenTypes.LITERAL_IF,
204 TokenTypes.LITERAL_SWITCH,
205 TokenTypes.LITERAL_SYNCHRONIZED,
206 TokenTypes.LITERAL_TRY,
207 TokenTypes.LITERAL_WHILE,
208 TokenTypes.METHOD_DEF,
209 TokenTypes.OBJBLOCK,
210 TokenTypes.STATIC_INIT,
211 TokenTypes.RECORD_DEF,
212 TokenTypes.COMPACT_CTOR_DEF,
213 };
214 }
215
216 @Override
217 public int[] getRequiredTokens() {
218 return CommonUtil.EMPTY_INT_ARRAY;
219 }
220
221
222
223
224
225
226
227
228
229
230 @Override
231 public void visitToken(DetailAST ast) {
232 final DetailAST startToken;
233 final DetailAST brace;
234
235 switch (ast.getType()) {
236 case TokenTypes.CTOR_DEF:
237 case TokenTypes.METHOD_DEF:
238 case TokenTypes.COMPACT_CTOR_DEF:
239 startToken = skipModifierAnnotations(ast);
240 brace = ast.findFirstToken(TokenTypes.SLIST);
241 break;
242 case TokenTypes.INTERFACE_DEF:
243 case TokenTypes.CLASS_DEF:
244 case TokenTypes.ANNOTATION_DEF:
245 case TokenTypes.ENUM_DEF:
246 case TokenTypes.ENUM_CONSTANT_DEF:
247 case TokenTypes.RECORD_DEF:
248 startToken = skipModifierAnnotations(ast);
249 brace = ast.findFirstToken(TokenTypes.OBJBLOCK);
250 break;
251 case TokenTypes.LITERAL_WHILE:
252 case TokenTypes.LITERAL_CATCH:
253 case TokenTypes.LITERAL_SYNCHRONIZED:
254 case TokenTypes.LITERAL_FOR:
255 case TokenTypes.LITERAL_TRY:
256 case TokenTypes.LITERAL_FINALLY:
257 case TokenTypes.LITERAL_DO:
258 case TokenTypes.LITERAL_IF:
259 case TokenTypes.STATIC_INIT:
260 case TokenTypes.LAMBDA:
261 startToken = ast;
262 brace = ast.findFirstToken(TokenTypes.SLIST);
263 break;
264 case TokenTypes.LITERAL_ELSE:
265 startToken = ast;
266 brace = getBraceAsFirstChild(ast);
267 break;
268 case TokenTypes.LITERAL_CASE:
269 case TokenTypes.LITERAL_DEFAULT:
270 startToken = ast;
271 brace = getBraceFromSwitchMember(ast);
272 break;
273 default:
274
275
276
277
278
279 startToken = ast;
280 brace = ast.findFirstToken(TokenTypes.LCURLY);
281 break;
282 }
283
284 if (brace != null) {
285 verifyBrace(brace, startToken);
286 }
287 }
288
289
290
291
292
293
294
295
296 @Nullable
297 private static DetailAST getBraceFromSwitchMember(DetailAST ast) {
298 final DetailAST brace;
299 final DetailAST parent = ast.getParent();
300 if (parent.getType() == TokenTypes.SWITCH_RULE) {
301 brace = parent.findFirstToken(TokenTypes.SLIST);
302 }
303 else {
304 brace = getBraceAsFirstChild(ast.getNextSibling());
305 }
306 return brace;
307 }
308
309
310
311
312
313
314
315
316 @Nullable
317 private static DetailAST getBraceAsFirstChild(DetailAST ast) {
318 DetailAST brace = null;
319 if (ast != null) {
320 final DetailAST candidate = ast.getFirstChild();
321 if (candidate != null && candidate.getType() == TokenTypes.SLIST) {
322 brace = candidate;
323 }
324 }
325 return brace;
326 }
327
328
329
330
331
332
333
334 private static DetailAST skipModifierAnnotations(DetailAST ast) {
335 DetailAST resultNode = ast;
336 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
337
338 if (modifiers != null) {
339 final DetailAST lastAnnotation = findLastAnnotation(modifiers);
340
341 if (lastAnnotation != null) {
342 if (lastAnnotation.getNextSibling() == null) {
343 resultNode = modifiers.getNextSibling();
344 }
345 else {
346 resultNode = lastAnnotation.getNextSibling();
347 }
348 }
349 }
350 return resultNode;
351 }
352
353
354
355
356
357
358
359
360 private static DetailAST findLastAnnotation(DetailAST modifiers) {
361 DetailAST annotation = modifiers.findFirstToken(TokenTypes.ANNOTATION);
362 while (annotation != null && annotation.getNextSibling() != null
363 && annotation.getNextSibling().getType() == TokenTypes.ANNOTATION) {
364 annotation = annotation.getNextSibling();
365 }
366 return annotation;
367 }
368
369
370
371
372
373
374
375
376 private void verifyBrace(final DetailAST brace,
377 final DetailAST startToken) {
378 final String braceLine = getLine(brace.getLineNo() - 1);
379
380
381 if (braceLine.length() <= brace.getColumnNo() + 1
382 || braceLine.charAt(brace.getColumnNo() + 1) != '}') {
383 if (option == LeftCurlyOption.NL) {
384 if (!CommonUtil.hasWhitespaceBefore(brace.getColumnNo(), braceLine)) {
385 log(brace, MSG_KEY_LINE_NEW, OPEN_CURLY_BRACE, brace.getColumnNo() + 1);
386 }
387 }
388 else if (option == LeftCurlyOption.EOL) {
389 validateEol(brace, braceLine);
390 }
391 else if (!TokenUtil.areOnSameLine(startToken, brace)) {
392 validateNewLinePosition(brace, startToken, braceLine);
393 }
394 }
395 }
396
397
398
399
400
401
402
403 private void validateEol(DetailAST brace, String braceLine) {
404 if (CommonUtil.hasWhitespaceBefore(brace.getColumnNo(), braceLine)) {
405 log(brace, MSG_KEY_LINE_PREVIOUS, OPEN_CURLY_BRACE, brace.getColumnNo() + 1);
406 }
407 if (!hasLineBreakAfter(brace)) {
408 log(brace, MSG_KEY_LINE_BREAK_AFTER, OPEN_CURLY_BRACE, brace.getColumnNo() + 1);
409 }
410 }
411
412
413
414
415
416
417
418
419 private void validateNewLinePosition(DetailAST brace, DetailAST startToken, String braceLine) {
420
421 if (startToken.getLineNo() + 1 == brace.getLineNo()) {
422 if (CommonUtil.hasWhitespaceBefore(brace.getColumnNo(), braceLine)) {
423 log(brace, MSG_KEY_LINE_PREVIOUS, OPEN_CURLY_BRACE, brace.getColumnNo() + 1);
424 }
425 else {
426 log(brace, MSG_KEY_LINE_NEW, OPEN_CURLY_BRACE, brace.getColumnNo() + 1);
427 }
428 }
429 else if (!CommonUtil.hasWhitespaceBefore(brace.getColumnNo(), braceLine)) {
430 log(brace, MSG_KEY_LINE_NEW, OPEN_CURLY_BRACE, brace.getColumnNo() + 1);
431 }
432 }
433
434
435
436
437
438
439
440
441
442 private boolean hasLineBreakAfter(DetailAST leftCurly) {
443 DetailAST nextToken = null;
444 if (leftCurly.getType() == TokenTypes.SLIST) {
445 nextToken = leftCurly.getFirstChild();
446 }
447 else {
448 if (!ignoreEnums
449 && leftCurly.getParent().getParent().getType() == TokenTypes.ENUM_DEF) {
450 nextToken = leftCurly.getNextSibling();
451 }
452 }
453 return nextToken == null
454 || nextToken.getType() == TokenTypes.RCURLY
455 || !TokenUtil.areOnSameLine(leftCurly, nextToken);
456 }
457
458 }