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