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