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 void visitToken(DetailAST ast)
152 {
153 final DetailAST startToken;
154 final DetailAST brace;
155
156 switch (ast.getType()) {
157 case TokenTypes.CTOR_DEF :
158 case TokenTypes.METHOD_DEF :
159 startToken = skipAnnotationOnlyLines(ast);
160 brace = ast.findFirstToken(TokenTypes.SLIST);
161 break;
162
163 case TokenTypes.INTERFACE_DEF :
164 case TokenTypes.CLASS_DEF :
165 case TokenTypes.ANNOTATION_DEF :
166 case TokenTypes.ENUM_DEF :
167 case TokenTypes.ENUM_CONSTANT_DEF :
168 startToken = skipAnnotationOnlyLines(ast);
169 final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
170 brace = (objBlock == null)
171 ? null
172 : objBlock.getFirstChild();
173 break;
174
175 case TokenTypes.LITERAL_WHILE:
176 case TokenTypes.LITERAL_CATCH:
177 case TokenTypes.LITERAL_SYNCHRONIZED:
178 case TokenTypes.LITERAL_FOR:
179 case TokenTypes.LITERAL_TRY:
180 case TokenTypes.LITERAL_FINALLY:
181 case TokenTypes.LITERAL_DO:
182 case TokenTypes.LITERAL_IF :
183 startToken = ast;
184 brace = ast.findFirstToken(TokenTypes.SLIST);
185 break;
186
187 case TokenTypes.LITERAL_ELSE :
188 startToken = ast;
189 final DetailAST candidate = ast.getFirstChild();
190 brace =
191 (candidate.getType() == TokenTypes.SLIST)
192 ? candidate
193 : null;
194 break;
195
196 case TokenTypes.LITERAL_SWITCH :
197 startToken = ast;
198 brace = ast.findFirstToken(TokenTypes.LCURLY);
199 break;
200
201 default :
202 startToken = null;
203 brace = null;
204 }
205
206 if ((brace != null) && (startToken != null)) {
207 verifyBrace(brace, startToken);
208 }
209 }
210
211
212
213
214
215
216
217
218
219
220
221 private DetailAST skipAnnotationOnlyLines(DetailAST ast)
222 {
223 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
224 if (modifiers == null) {
225 return ast;
226 }
227 DetailAST lastAnnot = findLastAnnotation(modifiers);
228 if (lastAnnot == null) {
229
230 return ast;
231 }
232 final DetailAST tokenAfterLast = lastAnnot.getNextSibling() != null
233 ? lastAnnot.getNextSibling()
234 : modifiers.getNextSibling();
235 if (tokenAfterLast.getLineNo() > lastAnnot.getLineNo()) {
236 return tokenAfterLast;
237 }
238 final int lastAnnotLineNumber = lastAnnot.getLineNo();
239 while (lastAnnot.getPreviousSibling() != null
240 && (lastAnnot.getPreviousSibling().getLineNo()
241 == lastAnnotLineNumber))
242 {
243 lastAnnot = lastAnnot.getPreviousSibling();
244 }
245 return lastAnnot;
246 }
247
248
249
250
251
252
253
254 private DetailAST findLastAnnotation(DetailAST modifiers)
255 {
256 DetailAST annot = modifiers.findFirstToken(TokenTypes.ANNOTATION);
257 while (annot != null && annot.getNextSibling() != null
258 && annot.getNextSibling().getType() == TokenTypes.ANNOTATION)
259 {
260 annot = annot.getNextSibling();
261 }
262 return annot;
263 }
264
265
266
267
268
269
270
271 private void verifyBrace(final DetailAST brace,
272 final DetailAST startToken)
273 {
274 final String braceLine = getLine(brace.getLineNo() - 1);
275
276
277
278
279 final int prevLineLen = (brace.getLineNo() == 1)
280 ? maxLineLength
281 : Utils.lengthMinusTrailingWhitespace(getLine(brace.getLineNo() - 2));
282
283
284 if ((braceLine.length() > (brace.getColumnNo() + 1))
285 && (braceLine.charAt(brace.getColumnNo() + 1) == '}'))
286 {
287 ;
288 }
289 else if (getAbstractOption() == LeftCurlyOption.NL) {
290 if (!Utils.whitespaceBefore(brace.getColumnNo(), braceLine)) {
291 log(brace.getLineNo(), brace.getColumnNo(),
292 MSG_KEY_LINE_NEW, "{");
293 }
294 }
295 else if (getAbstractOption() == LeftCurlyOption.EOL) {
296 if (Utils.whitespaceBefore(brace.getColumnNo(), braceLine)
297 && ((prevLineLen + 2) <= maxLineLength))
298 {
299 log(brace.getLineNo(), brace.getColumnNo(),
300 MSG_KEY_LINE_PREVIOUS, "{");
301 }
302 if (!hasLineBreakAfter(brace)) {
303 log(brace.getLineNo(), brace.getColumnNo(), MSG_KEY_LINE_BREAK_AFTER);
304 }
305 }
306 else if (getAbstractOption() == LeftCurlyOption.NLOW) {
307 if (startToken.getLineNo() == brace.getLineNo()) {
308 ;
309 }
310 else if ((startToken.getLineNo() + 1) == brace.getLineNo()) {
311 if (!Utils.whitespaceBefore(brace.getColumnNo(), braceLine)) {
312 log(brace.getLineNo(), brace.getColumnNo(),
313 MSG_KEY_LINE_NEW, "{");
314 }
315 else if ((prevLineLen + 2) <= maxLineLength) {
316 log(brace.getLineNo(), brace.getColumnNo(),
317 MSG_KEY_LINE_PREVIOUS, "{");
318 }
319 }
320 else if (!Utils.whitespaceBefore(brace.getColumnNo(), braceLine)) {
321 log(brace.getLineNo(), brace.getColumnNo(),
322 MSG_KEY_LINE_NEW, "{");
323 }
324 }
325 }
326
327
328
329
330
331
332
333
334 private boolean hasLineBreakAfter(DetailAST leftCurly)
335 {
336 DetailAST nextToken = null;
337 if (leftCurly.getType() == TokenTypes.SLIST) {
338 nextToken = leftCurly.getFirstChild();
339 }
340 else {
341 if (leftCurly.getParent().getParent().getType() == TokenTypes.ENUM_DEF)
342 {
343 if (!ignoreEnums) {
344 nextToken = leftCurly.getNextSibling();
345 }
346 }
347 }
348 if (nextToken != null && nextToken.getType() != TokenTypes.RCURLY) {
349 if (leftCurly.getLineNo() == nextToken.getLineNo()) {
350 return false;
351 }
352 }
353 return true;
354 }
355 }