1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle.checks.blocks;
20
21 import com.puppycrawl.tools.checkstyle.api.DetailAST;
22 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
23 import com.puppycrawl.tools.checkstyle.api.Utils;
24 import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck;
25
26
27
28
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 public class LeftCurlyCheck
76 extends AbstractOptionCheck<LeftCurlyOption>
77 {
78
79 private static final int DEFAULT_MAX_LINE_LENGTH = 80;
80
81
82
83
84
85 public static final String MSG_KEY_LINE_NEW = "line.new";
86
87
88
89
90
91 public static final String MSG_KEY_LINE_PREVIOUS = "line.previous";
92
93
94
95
96
97 public static final String MSG_KEY_LINE_BREAK_AFTER = "line.break.after";
98
99
100 private int maxLineLength = DEFAULT_MAX_LINE_LENGTH;
101
102
103 private boolean ignoreEnums = true;
104
105
106
107
108 public LeftCurlyCheck()
109 {
110 super(LeftCurlyOption.EOL, LeftCurlyOption.class);
111 }
112
113
114
115
116
117
118 public void setMaxLineLength(int maxLineLength)
119 {
120 this.maxLineLength = maxLineLength;
121 }
122
123 @Override
124 public int[] getDefaultTokens()
125 {
126 return new int[] {
127 TokenTypes.INTERFACE_DEF,
128 TokenTypes.CLASS_DEF,
129 TokenTypes.ANNOTATION_DEF,
130 TokenTypes.ENUM_DEF,
131 TokenTypes.CTOR_DEF,
132 TokenTypes.METHOD_DEF,
133 TokenTypes.ENUM_CONSTANT_DEF,
134 TokenTypes.LITERAL_WHILE,
135 TokenTypes.LITERAL_TRY,
136 TokenTypes.LITERAL_CATCH,
137 TokenTypes.LITERAL_FINALLY,
138 TokenTypes.LITERAL_SYNCHRONIZED,
139 TokenTypes.LITERAL_SWITCH,
140 TokenTypes.LITERAL_DO,
141 TokenTypes.LITERAL_IF,
142 TokenTypes.LITERAL_ELSE,
143 TokenTypes.LITERAL_FOR,
144
145
146 };
147 }
148
149 @Override
150 public int[] getAcceptableTokens()
151 {
152 return new int[] {
153 TokenTypes.INTERFACE_DEF,
154 TokenTypes.CLASS_DEF,
155 TokenTypes.ANNOTATION_DEF,
156 TokenTypes.ENUM_DEF,
157 TokenTypes.CTOR_DEF,
158 TokenTypes.METHOD_DEF,
159 TokenTypes.ENUM_CONSTANT_DEF,
160 TokenTypes.LITERAL_WHILE,
161 TokenTypes.LITERAL_TRY,
162 TokenTypes.LITERAL_CATCH,
163 TokenTypes.LITERAL_FINALLY,
164 TokenTypes.LITERAL_SYNCHRONIZED,
165 TokenTypes.LITERAL_SWITCH,
166 TokenTypes.LITERAL_DO,
167 TokenTypes.LITERAL_IF,
168 TokenTypes.LITERAL_ELSE,
169 TokenTypes.LITERAL_FOR,
170 };
171 }
172
173 @Override
174 public void visitToken(DetailAST ast)
175 {
176 final DetailAST startToken;
177 final DetailAST brace;
178
179 switch (ast.getType()) {
180 case TokenTypes.CTOR_DEF :
181 case TokenTypes.METHOD_DEF :
182 startToken = skipAnnotationOnlyLines(ast);
183 brace = ast.findFirstToken(TokenTypes.SLIST);
184 break;
185
186 case TokenTypes.INTERFACE_DEF :
187 case TokenTypes.CLASS_DEF :
188 case TokenTypes.ANNOTATION_DEF :
189 case TokenTypes.ENUM_DEF :
190 case TokenTypes.ENUM_CONSTANT_DEF :
191 startToken = skipAnnotationOnlyLines(ast);
192 final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
193 brace = objBlock == null
194 ? null
195 : objBlock.getFirstChild();
196 break;
197
198 case TokenTypes.LITERAL_WHILE:
199 case TokenTypes.LITERAL_CATCH:
200 case TokenTypes.LITERAL_SYNCHRONIZED:
201 case TokenTypes.LITERAL_FOR:
202 case TokenTypes.LITERAL_TRY:
203 case TokenTypes.LITERAL_FINALLY:
204 case TokenTypes.LITERAL_DO:
205 case TokenTypes.LITERAL_IF :
206 startToken = ast;
207 brace = ast.findFirstToken(TokenTypes.SLIST);
208 break;
209
210 case TokenTypes.LITERAL_ELSE :
211 startToken = ast;
212 final DetailAST candidate = ast.getFirstChild();
213 brace =
214 candidate.getType() == TokenTypes.SLIST
215 ? candidate
216 : null;
217 break;
218
219 case TokenTypes.LITERAL_SWITCH :
220 startToken = ast;
221 brace = ast.findFirstToken(TokenTypes.LCURLY);
222 break;
223
224 default :
225 startToken = null;
226 brace = null;
227 }
228
229 if (brace != null && startToken != null) {
230 verifyBrace(brace, startToken);
231 }
232 }
233
234
235
236
237
238
239
240
241
242
243
244 private DetailAST skipAnnotationOnlyLines(DetailAST ast)
245 {
246 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
247 if (modifiers == null) {
248 return ast;
249 }
250 DetailAST lastAnnot = findLastAnnotation(modifiers);
251 if (lastAnnot == null) {
252
253 return ast;
254 }
255 final DetailAST tokenAfterLast = lastAnnot.getNextSibling() != null
256 ? lastAnnot.getNextSibling()
257 : modifiers.getNextSibling();
258 if (tokenAfterLast.getLineNo() > lastAnnot.getLineNo()) {
259 return tokenAfterLast;
260 }
261 final int lastAnnotLineNumber = lastAnnot.getLineNo();
262 while (lastAnnot.getPreviousSibling() != null
263 && lastAnnot.getPreviousSibling().getLineNo()
264 == lastAnnotLineNumber)
265 {
266 lastAnnot = lastAnnot.getPreviousSibling();
267 }
268 return lastAnnot;
269 }
270
271
272
273
274
275
276
277 private DetailAST findLastAnnotation(DetailAST modifiers)
278 {
279 DetailAST annot = modifiers.findFirstToken(TokenTypes.ANNOTATION);
280 while (annot != null && annot.getNextSibling() != null
281 && annot.getNextSibling().getType() == TokenTypes.ANNOTATION)
282 {
283 annot = annot.getNextSibling();
284 }
285 return annot;
286 }
287
288
289
290
291
292
293
294 private void verifyBrace(final DetailAST brace,
295 final DetailAST startToken)
296 {
297 final String braceLine = getLine(brace.getLineNo() - 1);
298
299
300
301
302 final int prevLineLen = brace.getLineNo() == 1
303 ? maxLineLength
304 : Utils.lengthMinusTrailingWhitespace(getLine(brace.getLineNo() - 2));
305
306
307 if (braceLine.length() > brace.getColumnNo() + 1
308 && braceLine.charAt(brace.getColumnNo() + 1) == '}')
309 {
310
311 }
312 else if (getAbstractOption() == LeftCurlyOption.NL) {
313 if (!Utils.whitespaceBefore(brace.getColumnNo(), braceLine)) {
314 log(brace.getLineNo(), brace.getColumnNo(),
315 MSG_KEY_LINE_NEW, "{");
316 }
317 }
318 else if (getAbstractOption() == LeftCurlyOption.EOL) {
319 if (Utils.whitespaceBefore(brace.getColumnNo(), braceLine)
320 && prevLineLen + 2 <= maxLineLength)
321 {
322 log(brace.getLineNo(), brace.getColumnNo(),
323 MSG_KEY_LINE_PREVIOUS, "{");
324 }
325 if (!hasLineBreakAfter(brace)) {
326 log(brace.getLineNo(), brace.getColumnNo(), MSG_KEY_LINE_BREAK_AFTER);
327 }
328 }
329 else if (getAbstractOption() == LeftCurlyOption.NLOW) {
330 if (startToken.getLineNo() == brace.getLineNo()) {
331
332 }
333 else if (startToken.getLineNo() + 1 == brace.getLineNo()) {
334 if (!Utils.whitespaceBefore(brace.getColumnNo(), braceLine)) {
335 log(brace.getLineNo(), brace.getColumnNo(),
336 MSG_KEY_LINE_NEW, "{");
337 }
338 else if (prevLineLen + 2 <= maxLineLength) {
339 log(brace.getLineNo(), brace.getColumnNo(),
340 MSG_KEY_LINE_PREVIOUS, "{");
341 }
342 }
343 else if (!Utils.whitespaceBefore(brace.getColumnNo(), braceLine)) {
344 log(brace.getLineNo(), brace.getColumnNo(),
345 MSG_KEY_LINE_NEW, "{");
346 }
347 }
348 }
349
350
351
352
353
354
355
356
357 private boolean hasLineBreakAfter(DetailAST leftCurly)
358 {
359 DetailAST nextToken = null;
360 if (leftCurly.getType() == TokenTypes.SLIST) {
361 nextToken = leftCurly.getFirstChild();
362 }
363 else {
364 if (leftCurly.getParent().getParent().getType() == TokenTypes.ENUM_DEF)
365 {
366 if (!ignoreEnums) {
367 nextToken = leftCurly.getNextSibling();
368 }
369 }
370 }
371 if (nextToken != null && nextToken.getType() != TokenTypes.RCURLY) {
372 if (leftCurly.getLineNo() == nextToken.getLineNo()) {
373 return false;
374 }
375 }
376 return true;
377 }
378 }