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 com.puppycrawl.tools.checkstyle.StatelessCheck;
25 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26 import com.puppycrawl.tools.checkstyle.api.DetailAST;
27 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
29 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30
31
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 @StatelessCheck
84 public class RightCurlyCheck extends AbstractCheck {
85
86
87
88
89
90 public static final String MSG_KEY_LINE_BREAK_BEFORE = "line.break.before";
91
92
93
94
95
96 public static final String MSG_KEY_LINE_ALONE = "line.alone";
97
98
99
100
101
102 public static final String MSG_KEY_LINE_SAME = "line.same";
103
104
105
106
107
108 private RightCurlyOption option = RightCurlyOption.SAME;
109
110
111
112
113
114
115
116 public void setOption(String optionStr) {
117 option = RightCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
118 }
119
120 @Override
121 public int[] getDefaultTokens() {
122 return new int[] {
123 TokenTypes.LITERAL_TRY,
124 TokenTypes.LITERAL_CATCH,
125 TokenTypes.LITERAL_FINALLY,
126 TokenTypes.LITERAL_IF,
127 TokenTypes.LITERAL_ELSE,
128 };
129 }
130
131 @Override
132 public int[] getAcceptableTokens() {
133 return new int[] {
134 TokenTypes.LITERAL_TRY,
135 TokenTypes.LITERAL_CATCH,
136 TokenTypes.LITERAL_FINALLY,
137 TokenTypes.LITERAL_IF,
138 TokenTypes.LITERAL_ELSE,
139 TokenTypes.CLASS_DEF,
140 TokenTypes.METHOD_DEF,
141 TokenTypes.CTOR_DEF,
142 TokenTypes.LITERAL_FOR,
143 TokenTypes.LITERAL_WHILE,
144 TokenTypes.LITERAL_DO,
145 TokenTypes.STATIC_INIT,
146 TokenTypes.INSTANCE_INIT,
147 };
148 }
149
150 @Override
151 public int[] getRequiredTokens() {
152 return CommonUtil.EMPTY_INT_ARRAY;
153 }
154
155 @Override
156 public void visitToken(DetailAST ast) {
157 final Details details = Details.getDetails(ast);
158 final DetailAST rcurly = details.rcurly;
159
160 if (rcurly != null) {
161 final String violation = validate(details);
162 if (!violation.isEmpty()) {
163 log(rcurly, violation, "}", rcurly.getColumnNo() + 1);
164 }
165 }
166 }
167
168
169
170
171
172
173
174 private String validate(Details details) {
175 String violation = "";
176 if (shouldHaveLineBreakBefore(option, details)) {
177 violation = MSG_KEY_LINE_BREAK_BEFORE;
178 }
179 else if (shouldBeOnSameLine(option, details)) {
180 violation = MSG_KEY_LINE_SAME;
181 }
182 else if (shouldBeAloneOnLine(option, details, getLine(details.rcurly.getLineNo() - 1))) {
183 violation = MSG_KEY_LINE_ALONE;
184 }
185 return violation;
186 }
187
188
189
190
191
192
193
194 private static boolean shouldHaveLineBreakBefore(RightCurlyOption bracePolicy,
195 Details details) {
196 return bracePolicy == RightCurlyOption.SAME
197 && !hasLineBreakBefore(details.rcurly)
198 && details.lcurly.getLineNo() != details.rcurly.getLineNo();
199 }
200
201
202
203
204
205
206
207 private static boolean shouldBeOnSameLine(RightCurlyOption bracePolicy, Details details) {
208 return bracePolicy == RightCurlyOption.SAME
209 && !details.shouldCheckLastRcurly
210 && details.rcurly.getLineNo() != details.nextToken.getLineNo();
211 }
212
213
214
215
216
217
218
219
220 private static boolean shouldBeAloneOnLine(RightCurlyOption bracePolicy,
221 Details details,
222 String targetSrcLine) {
223 return bracePolicy == RightCurlyOption.ALONE
224 && shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
225 || bracePolicy == RightCurlyOption.ALONE_OR_SINGLELINE
226 && shouldBeAloneOnLineWithAloneOrSinglelineOption(details, targetSrcLine)
227 || details.shouldCheckLastRcurly
228 && details.rcurly.getLineNo() == details.nextToken.getLineNo();
229 }
230
231
232
233
234
235
236
237 private static boolean shouldBeAloneOnLineWithAloneOption(Details details,
238 String targetSrcLine) {
239 return !isAloneOnLine(details, targetSrcLine);
240 }
241
242
243
244
245
246
247
248
249 private static boolean shouldBeAloneOnLineWithAloneOrSinglelineOption(Details details,
250 String targetSrcLine) {
251 return !isAloneOnLine(details, targetSrcLine)
252 && !isSingleLineBlock(details)
253 && !isEmptyBody(details.lcurly);
254 }
255
256
257
258
259
260
261
262 private static boolean isAloneOnLine(Details details, String targetSrcLine) {
263 final DetailAST rcurly = details.rcurly;
264 final DetailAST nextToken = details.nextToken;
265 return (rcurly.getLineNo() != nextToken.getLineNo() || skipDoubleBraceInstInit(details))
266 && CommonUtil.hasWhitespaceBefore(details.rcurly.getColumnNo(), targetSrcLine);
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 private static boolean skipDoubleBraceInstInit(Details details) {
288 final DetailAST rcurly = details.rcurly;
289 final DetailAST tokenAfterNextToken = Details.getNextToken(details.nextToken);
290 return rcurly.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
291 && details.nextToken.getType() == TokenTypes.RCURLY
292 && tokenAfterNextToken.getType() == TokenTypes.SEMI
293 && rcurly.getLineNo() != Details.getNextToken(tokenAfterNextToken).getLineNo();
294
295 }
296
297
298
299
300
301
302 private static boolean isSingleLineBlock(Details details) {
303 final DetailAST rcurly = details.rcurly;
304 final DetailAST lcurly = details.lcurly;
305 DetailAST nextToken = details.nextToken;
306 while (nextToken.getType() == TokenTypes.LITERAL_ELSE) {
307 nextToken = Details.getNextToken(nextToken);
308 }
309 if (nextToken.getType() == TokenTypes.DO_WHILE) {
310 final DetailAST doWhileSemi = nextToken.getParent().getLastChild();
311 nextToken = Details.getNextToken(doWhileSemi);
312 }
313 return rcurly.getLineNo() == lcurly.getLineNo()
314 && rcurly.getLineNo() != nextToken.getLineNo();
315 }
316
317
318
319
320
321
322 private static boolean isEmptyBody(DetailAST lcurly) {
323 boolean result = false;
324 if (lcurly.getParent().getType() == TokenTypes.OBJBLOCK) {
325 if (lcurly.getNextSibling().getType() == TokenTypes.RCURLY) {
326 result = true;
327 }
328 }
329 else if (lcurly.getFirstChild().getType() == TokenTypes.RCURLY) {
330 result = true;
331 }
332 return result;
333 }
334
335
336
337
338
339
340 private static boolean hasLineBreakBefore(DetailAST rightCurly) {
341 final DetailAST previousToken = rightCurly.getPreviousSibling();
342 return previousToken == null
343 || rightCurly.getLineNo() != previousToken.getLineNo();
344 }
345
346
347
348
349 private static final class Details {
350
351
352 private final DetailAST rcurly;
353
354 private final DetailAST lcurly;
355
356 private final DetailAST nextToken;
357
358 private final boolean shouldCheckLastRcurly;
359
360
361
362
363
364
365
366
367 private Details(DetailAST lcurly, DetailAST rcurly,
368 DetailAST nextToken, boolean shouldCheckLastRcurly) {
369 this.lcurly = lcurly;
370 this.rcurly = rcurly;
371 this.nextToken = nextToken;
372 this.shouldCheckLastRcurly = shouldCheckLastRcurly;
373 }
374
375
376
377
378
379
380 private static Details getDetails(DetailAST ast) {
381 final Details details;
382 switch (ast.getType()) {
383 case TokenTypes.LITERAL_TRY:
384 case TokenTypes.LITERAL_CATCH:
385 case TokenTypes.LITERAL_FINALLY:
386 details = getDetailsForTryCatchFinally(ast);
387 break;
388 case TokenTypes.LITERAL_IF:
389 case TokenTypes.LITERAL_ELSE:
390 details = getDetailsForIfElse(ast);
391 break;
392 case TokenTypes.LITERAL_DO:
393 case TokenTypes.LITERAL_WHILE:
394 case TokenTypes.LITERAL_FOR:
395 details = getDetailsForLoops(ast);
396 break;
397 default:
398 details = getDetailsForOthers(ast);
399 break;
400 }
401 return details;
402 }
403
404
405
406
407
408
409 private static Details getDetailsForTryCatchFinally(DetailAST ast) {
410 boolean shouldCheckLastRcurly = false;
411 final DetailAST rcurly;
412 final DetailAST lcurly;
413 DetailAST nextToken;
414 final int tokenType = ast.getType();
415 if (tokenType == TokenTypes.LITERAL_TRY) {
416 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
417 lcurly = ast.getFirstChild().getNextSibling();
418 }
419 else {
420 lcurly = ast.getFirstChild();
421 }
422 nextToken = lcurly.getNextSibling();
423 rcurly = lcurly.getLastChild();
424
425 if (nextToken == null) {
426 shouldCheckLastRcurly = true;
427 nextToken = getNextToken(ast);
428 }
429 }
430 else if (tokenType == TokenTypes.LITERAL_CATCH) {
431 nextToken = ast.getNextSibling();
432 lcurly = ast.getLastChild();
433 rcurly = lcurly.getLastChild();
434 if (nextToken == null) {
435 shouldCheckLastRcurly = true;
436 nextToken = getNextToken(ast);
437 }
438 }
439 else {
440 shouldCheckLastRcurly = true;
441 nextToken = getNextToken(ast);
442 lcurly = ast.getFirstChild();
443 rcurly = lcurly.getLastChild();
444 }
445 return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
446 }
447
448
449
450
451
452
453 private static Details getDetailsForIfElse(DetailAST ast) {
454 boolean shouldCheckLastRcurly = false;
455 final DetailAST lcurly;
456 DetailAST nextToken;
457 final int tokenType = ast.getType();
458 if (tokenType == TokenTypes.LITERAL_IF) {
459 nextToken = ast.findFirstToken(TokenTypes.LITERAL_ELSE);
460 if (nextToken == null) {
461 shouldCheckLastRcurly = true;
462 nextToken = getNextToken(ast);
463 lcurly = ast.getLastChild();
464 }
465 else {
466 lcurly = nextToken.getPreviousSibling();
467 }
468 }
469 else {
470 shouldCheckLastRcurly = true;
471 nextToken = getNextToken(ast);
472 lcurly = ast.getFirstChild();
473 }
474 DetailAST rcurly = null;
475 if (lcurly.getType() == TokenTypes.SLIST) {
476 rcurly = lcurly.getLastChild();
477 }
478 return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
479 }
480
481
482
483
484
485
486
487 private static Details getDetailsForOthers(DetailAST ast) {
488 DetailAST rcurly = null;
489 final DetailAST lcurly;
490 final int tokenType = ast.getType();
491 if (tokenType == TokenTypes.CLASS_DEF) {
492 final DetailAST child = ast.getLastChild();
493 lcurly = child.getFirstChild();
494 rcurly = child.getLastChild();
495 }
496 else {
497 lcurly = ast.findFirstToken(TokenTypes.SLIST);
498 if (lcurly != null) {
499
500 rcurly = lcurly.getLastChild();
501 }
502 }
503 return new Details(lcurly, rcurly, getNextToken(ast), false);
504 }
505
506
507
508
509
510
511 private static Details getDetailsForLoops(DetailAST ast) {
512 DetailAST rcurly = null;
513 final DetailAST lcurly;
514 final DetailAST nextToken;
515 final int tokenType = ast.getType();
516 if (tokenType == TokenTypes.LITERAL_DO) {
517 nextToken = ast.findFirstToken(TokenTypes.DO_WHILE);
518 lcurly = ast.findFirstToken(TokenTypes.SLIST);
519 if (lcurly != null) {
520 rcurly = lcurly.getLastChild();
521 }
522 }
523 else {
524 lcurly = ast.findFirstToken(TokenTypes.SLIST);
525 if (lcurly != null) {
526
527 rcurly = lcurly.getLastChild();
528 }
529 nextToken = getNextToken(ast);
530 }
531 return new Details(lcurly, rcurly, nextToken, false);
532 }
533
534
535
536
537
538
539 private static DetailAST getNextToken(DetailAST ast) {
540 DetailAST next = null;
541 DetailAST parent = ast;
542 while (next == null && parent != null) {
543 next = parent.getNextSibling();
544 parent = parent.getParent();
545 }
546 if (next == null) {
547
548
549 next = new DetailAST();
550 }
551 else {
552 next = CheckUtil.getFirstNode(next);
553 }
554 return next;
555 }
556
557 }
558
559 }