1 | /////////////////////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code and other text files for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2022 the original author or authors. | |
4 | // | |
5 | // This library is free software; you can redistribute it and/or | |
6 | // modify it under the terms of the GNU Lesser General Public | |
7 | // License as published by the Free Software Foundation; either | |
8 | // version 2.1 of the License, or (at your option) any later version. | |
9 | // | |
10 | // This library is distributed in the hope that it will be useful, | |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | // Lesser General Public License for more details. | |
14 | // | |
15 | // You should have received a copy of the GNU Lesser General Public | |
16 | // License along with this library; if not, write to the Free Software | |
17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | /////////////////////////////////////////////////////////////////////////////////////////////// | |
19 | ||
20 | package com.puppycrawl.tools.checkstyle.checks.blocks; | |
21 | ||
22 | import java.util.Arrays; | |
23 | import java.util.Locale; | |
24 | ||
25 | import com.puppycrawl.tools.checkstyle.StatelessCheck; | |
26 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
27 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
28 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
29 | import com.puppycrawl.tools.checkstyle.utils.CommonUtil; | |
30 | import com.puppycrawl.tools.checkstyle.utils.TokenUtil; | |
31 | ||
32 | /** | |
33 | * <p> | |
34 | * Checks the placement of right curly braces ({@code '}'}) for code blocks. This check supports | |
35 | * if-else, try-catch-finally blocks, while-loops, for-loops, | |
36 | * method definitions, class definitions, constructor definitions, | |
37 | * instance, static initialization blocks, annotation definitions and enum definitions. | |
38 | * For right curly brace of expression blocks of arrays, lambdas and class instances | |
39 | * please follow issue | |
40 | * <a href="https://github.com/checkstyle/checkstyle/issues/5945">#5945</a>. | |
41 | * For right curly brace of enum constant please follow issue | |
42 | * <a href="https://github.com/checkstyle/checkstyle/issues/7519">#7519</a>. | |
43 | * </p> | |
44 | * <ul> | |
45 | * <li> | |
46 | * Property {@code option} - Specify the policy on placement of a right curly brace | |
47 | * (<code>'}'</code>). | |
48 | * Type is {@code com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyOption}. | |
49 | * Default value is {@code same}. | |
50 | * </li> | |
51 | * <li> | |
52 | * Property {@code tokens} - tokens to check | |
53 | * Type is {@code java.lang.String[]}. | |
54 | * Validation type is {@code tokenSet}. | |
55 | * Default value is: | |
56 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY"> | |
57 | * LITERAL_TRY</a>, | |
58 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH"> | |
59 | * LITERAL_CATCH</a>, | |
60 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY"> | |
61 | * LITERAL_FINALLY</a>, | |
62 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF"> | |
63 | * LITERAL_IF</a>, | |
64 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE"> | |
65 | * LITERAL_ELSE</a>. | |
66 | * </li> | |
67 | * </ul> | |
68 | * <p> | |
69 | * To configure the check: | |
70 | * </p> | |
71 | * <pre> | |
72 | * <module name="RightCurly"/> | |
73 | * </pre> | |
74 | * <p> | |
75 | * Example: | |
76 | * </p> | |
77 | * <pre> | |
78 | * public class Test { | |
79 | * | |
80 | * public void test() { | |
81 | * | |
82 | * if (foo) { | |
83 | * bar(); | |
84 | * } // violation, right curly must be in the same line as the 'else' keyword | |
85 | * else { | |
86 | * bar(); | |
87 | * } | |
88 | * | |
89 | * if (foo) { | |
90 | * bar(); | |
91 | * } else { // OK | |
92 | * bar(); | |
93 | * } | |
94 | * | |
95 | * if (foo) { bar(); } int i = 0; // violation | |
96 | * // ^^^ statement is not allowed on same line after curly right brace | |
97 | * | |
98 | * if (foo) { bar(); } // OK | |
99 | * int i = 0; | |
100 | * | |
101 | * try { | |
102 | * bar(); | |
103 | * } // violation, rightCurly must be in the same line as 'catch' keyword | |
104 | * catch (Exception e) { | |
105 | * bar(); | |
106 | * } | |
107 | * | |
108 | * try { | |
109 | * bar(); | |
110 | * } catch (Exception e) { // OK | |
111 | * bar(); | |
112 | * } | |
113 | * | |
114 | * } // OK | |
115 | * | |
116 | * public void testSingleLine() { bar(); } // OK, because singleline is allowed | |
117 | * } | |
118 | * </pre> | |
119 | * <p> | |
120 | * To configure the check with policy {@code alone} for {@code else} and | |
121 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> | |
122 | * METHOD_DEF</a> tokens: | |
123 | * </p> | |
124 | * <pre> | |
125 | * <module name="RightCurly"> | |
126 | * <property name="option" value="alone"/> | |
127 | * <property name="tokens" value="LITERAL_ELSE, METHOD_DEF"/> | |
128 | * </module> | |
129 | * </pre> | |
130 | * <p> | |
131 | * Example: | |
132 | * </p> | |
133 | * <pre> | |
134 | * public class Test { | |
135 | * | |
136 | * public void test() { | |
137 | * | |
138 | * if (foo) { | |
139 | * bar(); | |
140 | * } else { bar(); } // violation, right curly must be alone on line | |
141 | * | |
142 | * if (foo) { | |
143 | * bar(); | |
144 | * } else { | |
145 | * bar(); | |
146 | * } // OK | |
147 | * | |
148 | * try { | |
149 | * bar(); | |
150 | * } catch (Exception e) { // OK because config is set to token METHOD_DEF and LITERAL_ELSE | |
151 | * bar(); | |
152 | * } | |
153 | * | |
154 | * } // OK | |
155 | * | |
156 | * public void violate() { bar; } // violation, singleline is not allowed here | |
157 | * | |
158 | * public void ok() { | |
159 | * bar(); | |
160 | * } // OK | |
161 | * } | |
162 | * </pre> | |
163 | * <p> | |
164 | * To configure the check with policy {@code alone_or_singleline} for {@code if} and | |
165 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> | |
166 | * METHOD_DEF</a> | |
167 | * tokens: | |
168 | * </p> | |
169 | * <pre> | |
170 | * <module name="RightCurly"> | |
171 | * <property name="option" value="alone_or_singleline"/> | |
172 | * <property name="tokens" value="LITERAL_IF, METHOD_DEF"/> | |
173 | * </module> | |
174 | * </pre> | |
175 | * <p> | |
176 | * Example: | |
177 | * </p> | |
178 | * <pre> | |
179 | * public class Test { | |
180 | * | |
181 | * public void test() { | |
182 | * | |
183 | * if (foo) { | |
184 | * bar(); | |
185 | * } else { // violation, right curly must be alone on line | |
186 | * bar(); | |
187 | * } | |
188 | * | |
189 | * if (foo) { | |
190 | * bar(); | |
191 | * } // OK | |
192 | * else { | |
193 | * bar(); | |
194 | * } | |
195 | * | |
196 | * try { | |
197 | * bar(); | |
198 | * } catch (Exception e) { // OK because config did not set token LITERAL_TRY | |
199 | * bar(); | |
200 | * } | |
201 | * | |
202 | * } // OK | |
203 | * | |
204 | * public void violate() { bar(); } // OK , because singleline | |
205 | * } | |
206 | * </pre> | |
207 | * <p> | |
208 | * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} | |
209 | * </p> | |
210 | * <p> | |
211 | * Violation Message Keys: | |
212 | * </p> | |
213 | * <ul> | |
214 | * <li> | |
215 | * {@code line.alone} | |
216 | * </li> | |
217 | * <li> | |
218 | * {@code line.break.before} | |
219 | * </li> | |
220 | * <li> | |
221 | * {@code line.same} | |
222 | * </li> | |
223 | * </ul> | |
224 | * | |
225 | * @since 3.0 | |
226 | */ | |
227 | @StatelessCheck | |
228 | public class RightCurlyCheck extends AbstractCheck { | |
229 | ||
230 | /** | |
231 | * A key is pointing to the warning message text in "messages.properties" | |
232 | * file. | |
233 | */ | |
234 | public static final String MSG_KEY_LINE_BREAK_BEFORE = "line.break.before"; | |
235 | ||
236 | /** | |
237 | * A key is pointing to the warning message text in "messages.properties" | |
238 | * file. | |
239 | */ | |
240 | public static final String MSG_KEY_LINE_ALONE = "line.alone"; | |
241 | ||
242 | /** | |
243 | * A key is pointing to the warning message text in "messages.properties" | |
244 | * file. | |
245 | */ | |
246 | public static final String MSG_KEY_LINE_SAME = "line.same"; | |
247 | ||
248 | /** | |
249 | * Specify the policy on placement of a right curly brace (<code>'}'</code>). | |
250 | */ | |
251 | private RightCurlyOption option = RightCurlyOption.SAME; | |
252 | ||
253 | /** | |
254 | * Setter to specify the policy on placement of a right curly brace (<code>'}'</code>). | |
255 | * | |
256 | * @param optionStr string to decode option from | |
257 | * @throws IllegalArgumentException if unable to decode | |
258 | */ | |
259 | public void setOption(String optionStr) { | |
260 | option = RightCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); | |
261 | } | |
262 | ||
263 | @Override | |
264 | public int[] getDefaultTokens() { | |
265 | return new int[] { | |
266 | TokenTypes.LITERAL_TRY, | |
267 | TokenTypes.LITERAL_CATCH, | |
268 | TokenTypes.LITERAL_FINALLY, | |
269 | TokenTypes.LITERAL_IF, | |
270 | TokenTypes.LITERAL_ELSE, | |
271 | }; | |
272 | } | |
273 | ||
274 | @Override | |
275 | public int[] getAcceptableTokens() { | |
276 | return new int[] { | |
277 | TokenTypes.LITERAL_TRY, | |
278 | TokenTypes.LITERAL_CATCH, | |
279 | TokenTypes.LITERAL_FINALLY, | |
280 | TokenTypes.LITERAL_IF, | |
281 | TokenTypes.LITERAL_ELSE, | |
282 | TokenTypes.CLASS_DEF, | |
283 | TokenTypes.METHOD_DEF, | |
284 | TokenTypes.CTOR_DEF, | |
285 | TokenTypes.LITERAL_FOR, | |
286 | TokenTypes.LITERAL_WHILE, | |
287 | TokenTypes.LITERAL_DO, | |
288 | TokenTypes.STATIC_INIT, | |
289 | TokenTypes.INSTANCE_INIT, | |
290 | TokenTypes.ANNOTATION_DEF, | |
291 | TokenTypes.ENUM_DEF, | |
292 | TokenTypes.INTERFACE_DEF, | |
293 | TokenTypes.RECORD_DEF, | |
294 | TokenTypes.COMPACT_CTOR_DEF, | |
295 | }; | |
296 | } | |
297 | ||
298 | @Override | |
299 | public int[] getRequiredTokens() { | |
300 | return CommonUtil.EMPTY_INT_ARRAY; | |
301 | } | |
302 | ||
303 | @Override | |
304 | public void visitToken(DetailAST ast) { | |
305 | final Details details = Details.getDetails(ast); | |
306 | final DetailAST rcurly = details.rcurly; | |
307 | ||
308 | if (rcurly != null) { | |
309 | final String violation = validate(details); | |
310 | if (!violation.isEmpty()) { | |
311 |
1
1. visitToken : Replaced integer addition with subtraction → KILLED |
log(rcurly, violation, "}", rcurly.getColumnNo() + 1); |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | /** | |
317 | * Does general validation. | |
318 | * | |
319 | * @param details for validation. | |
320 | * @return violation message or empty string | |
321 | * if there was no violation during validation. | |
322 | */ | |
323 | private String validate(Details details) { | |
324 | String violation = ""; | |
325 | if (shouldHaveLineBreakBefore(option, details)) { | |
326 | violation = MSG_KEY_LINE_BREAK_BEFORE; | |
327 | } | |
328 | else if (shouldBeOnSameLine(option, details)) { | |
329 | violation = MSG_KEY_LINE_SAME; | |
330 | } | |
331 |
1
1. validate : Replaced integer subtraction with addition → KILLED |
else if (shouldBeAloneOnLine(option, details, getLine(details.rcurly.getLineNo() - 1))) { |
332 | violation = MSG_KEY_LINE_ALONE; | |
333 | } | |
334 | return violation; | |
335 | } | |
336 | ||
337 | /** | |
338 | * Checks whether a right curly should have a line break before. | |
339 | * | |
340 | * @param bracePolicy option for placing the right curly brace. | |
341 | * @param details details for validation. | |
342 | * @return true if a right curly should have a line break before. | |
343 | */ | |
344 | private static boolean shouldHaveLineBreakBefore(RightCurlyOption bracePolicy, | |
345 | Details details) { | |
346 | return bracePolicy == RightCurlyOption.SAME | |
347 | && !hasLineBreakBefore(details.rcurly) | |
348 | && !TokenUtil.areOnSameLine(details.lcurly, details.rcurly); | |
349 | } | |
350 | ||
351 | /** | |
352 | * Checks that a right curly should be on the same line as the next statement. | |
353 | * | |
354 | * @param bracePolicy option for placing the right curly brace | |
355 | * @param details Details for validation | |
356 | * @return true if a right curly should be alone on a line. | |
357 | */ | |
358 | private static boolean shouldBeOnSameLine(RightCurlyOption bracePolicy, Details details) { | |
359 | return bracePolicy == RightCurlyOption.SAME | |
360 | && !details.shouldCheckLastRcurly | |
361 | && !TokenUtil.areOnSameLine(details.rcurly, details.nextToken); | |
362 | } | |
363 | ||
364 | /** | |
365 | * Checks that a right curly should be alone on a line. | |
366 | * | |
367 | * @param bracePolicy option for placing the right curly brace | |
368 | * @param details Details for validation | |
369 | * @param targetSrcLine A string with contents of rcurly's line | |
370 | * @return true if a right curly should be alone on a line. | |
371 | */ | |
372 | private static boolean shouldBeAloneOnLine(RightCurlyOption bracePolicy, | |
373 | Details details, | |
374 | String targetSrcLine) { | |
375 | return bracePolicy == RightCurlyOption.ALONE | |
376 | && shouldBeAloneOnLineWithAloneOption(details, targetSrcLine) | |
377 | || (bracePolicy == RightCurlyOption.ALONE_OR_SINGLELINE | |
378 | || details.shouldCheckLastRcurly) | |
379 | && shouldBeAloneOnLineWithNotAloneOption(details, targetSrcLine); | |
380 | } | |
381 | ||
382 | /** | |
383 | * Whether right curly should be alone on line when ALONE option is used. | |
384 | * | |
385 | * @param details details for validation. | |
386 | * @param targetSrcLine A string with contents of rcurly's line | |
387 | * @return true, if right curly should be alone on line when ALONE option is used. | |
388 | */ | |
389 | private static boolean shouldBeAloneOnLineWithAloneOption(Details details, | |
390 | String targetSrcLine) { | |
391 | return !isAloneOnLine(details, targetSrcLine); | |
392 | } | |
393 | ||
394 | /** | |
395 | * Whether right curly should be alone on line when ALONE_OR_SINGLELINE or SAME option is used. | |
396 | * | |
397 | * @param details details for validation. | |
398 | * @param targetSrcLine A string with contents of rcurly's line | |
399 | * @return true, if right curly should be alone on line | |
400 | * when ALONE_OR_SINGLELINE or SAME option is used. | |
401 | */ | |
402 | private static boolean shouldBeAloneOnLineWithNotAloneOption(Details details, | |
403 | String targetSrcLine) { | |
404 | return shouldBeAloneOnLineWithAloneOption(details, targetSrcLine) | |
405 | && !isBlockAloneOnSingleLine(details); | |
406 | } | |
407 | ||
408 | /** | |
409 | * Checks whether right curly is alone on a line. | |
410 | * | |
411 | * @param details for validation. | |
412 | * @param targetSrcLine A string with contents of rcurly's line | |
413 | * @return true if right curly is alone on a line. | |
414 | */ | |
415 | private static boolean isAloneOnLine(Details details, String targetSrcLine) { | |
416 | final DetailAST rcurly = details.rcurly; | |
417 | final DetailAST nextToken = details.nextToken; | |
418 | return (nextToken == null || !TokenUtil.areOnSameLine(rcurly, nextToken) | |
419 | || skipDoubleBraceInstInit(details)) | |
420 | && CommonUtil.hasWhitespaceBefore(details.rcurly.getColumnNo(), | |
421 | targetSrcLine); | |
422 | } | |
423 | ||
424 | /** | |
425 | * This method determines if the double brace initialization should be skipped over by the | |
426 | * check. Double brace initializations are treated differently. The corresponding inner | |
427 | * rcurly is treated as if it was alone on line even when it may be followed by another | |
428 | * rcurly and a semi, raising no violations. | |
429 | * <i>Please do note though that the line should not contain anything other than the following | |
430 | * right curly and the semi following it or else violations will be raised.</i> | |
431 | * Only the kind of double brace initializations shown in the following example code will be | |
432 | * skipped over:<br> | |
433 | * <pre> | |
434 | * {@code Map<String, String> map = new LinkedHashMap<>() {{ | |
435 | * put("alpha", "man"); | |
436 | * }}; // no violation} | |
437 | * </pre> | |
438 | * | |
439 | * @param details {@link Details} object containing the details relevant to the rcurly | |
440 | * @return if the double brace initialization rcurly should be skipped over by the check | |
441 | */ | |
442 | private static boolean skipDoubleBraceInstInit(Details details) { | |
443 | boolean skipDoubleBraceInstInit = false; | |
444 | final DetailAST tokenAfterNextToken = Details.getNextToken(details.nextToken); | |
445 | if (tokenAfterNextToken != null) { | |
446 | final DetailAST rcurly = details.rcurly; | |
447 | skipDoubleBraceInstInit = rcurly.getParent().getParent() | |
448 | .getType() == TokenTypes.INSTANCE_INIT | |
449 | && details.nextToken.getType() == TokenTypes.RCURLY | |
450 | && !TokenUtil.areOnSameLine(rcurly, Details.getNextToken(tokenAfterNextToken)); | |
451 | } | |
452 | return skipDoubleBraceInstInit; | |
453 | } | |
454 | ||
455 | /** | |
456 | * Checks whether block has a single-line format and is alone on a line. | |
457 | * | |
458 | * @param details for validation. | |
459 | * @return true if block has single-line format and is alone on a line. | |
460 | */ | |
461 | private static boolean isBlockAloneOnSingleLine(Details details) { | |
462 | DetailAST nextToken = details.nextToken; | |
463 | ||
464 | while (nextToken != null && nextToken.getType() == TokenTypes.LITERAL_ELSE) { | |
465 | nextToken = Details.getNextToken(nextToken); | |
466 | } | |
467 | ||
468 | if (nextToken != null && nextToken.getType() == TokenTypes.DO_WHILE) { | |
469 | final DetailAST doWhileSemi = nextToken.getParent().getLastChild(); | |
470 | nextToken = Details.getNextToken(doWhileSemi); | |
471 | } | |
472 | ||
473 | return TokenUtil.areOnSameLine(details.lcurly, details.rcurly) | |
474 | && (nextToken == null || !TokenUtil.areOnSameLine(details.rcurly, nextToken) | |
475 | || isRightcurlyFollowedBySemicolon(details)); | |
476 | } | |
477 | ||
478 | /** | |
479 | * Checks whether the right curly is followed by a semicolon. | |
480 | * | |
481 | * @param details details for validation. | |
482 | * @return true if the right curly is followed by a semicolon. | |
483 | */ | |
484 | private static boolean isRightcurlyFollowedBySemicolon(Details details) { | |
485 | return details.nextToken.getType() == TokenTypes.SEMI; | |
486 | } | |
487 | ||
488 | /** | |
489 | * Checks if right curly has line break before. | |
490 | * | |
491 | * @param rightCurly right curly token. | |
492 | * @return true, if right curly has line break before. | |
493 | */ | |
494 | private static boolean hasLineBreakBefore(DetailAST rightCurly) { | |
495 | DetailAST previousToken = rightCurly.getPreviousSibling(); | |
496 | if (previousToken == null) { | |
497 | previousToken = rightCurly.getParent(); | |
498 | } | |
499 | return !TokenUtil.areOnSameLine(rightCurly, previousToken); | |
500 | } | |
501 | ||
502 | /** | |
503 | * Structure that contains all details for validation. | |
504 | */ | |
505 | private static final class Details { | |
506 | ||
507 | /** | |
508 | * Token types that identify tokens that will never have SLIST in their AST. | |
509 | */ | |
510 | private static final int[] TOKENS_WITH_NO_CHILD_SLIST = { | |
511 | TokenTypes.CLASS_DEF, | |
512 | TokenTypes.ENUM_DEF, | |
513 | TokenTypes.ANNOTATION_DEF, | |
514 | TokenTypes.INTERFACE_DEF, | |
515 | TokenTypes.RECORD_DEF, | |
516 | }; | |
517 | ||
518 | /** Right curly. */ | |
519 | private final DetailAST rcurly; | |
520 | /** Left curly. */ | |
521 | private final DetailAST lcurly; | |
522 | /** Next token. */ | |
523 | private final DetailAST nextToken; | |
524 | /** Should check last right curly. */ | |
525 | private final boolean shouldCheckLastRcurly; | |
526 | ||
527 | /** | |
528 | * Constructor. | |
529 | * | |
530 | * @param lcurly the lcurly of the token whose details are being collected | |
531 | * @param rcurly the rcurly of the token whose details are being collected | |
532 | * @param nextToken the token after the token whose details are being collected | |
533 | * @param shouldCheckLastRcurly boolean value to determine if to check last rcurly | |
534 | */ | |
535 | private Details(DetailAST lcurly, DetailAST rcurly, | |
536 | DetailAST nextToken, boolean shouldCheckLastRcurly) { | |
537 | this.lcurly = lcurly; | |
538 | this.rcurly = rcurly; | |
539 | this.nextToken = nextToken; | |
540 | this.shouldCheckLastRcurly = shouldCheckLastRcurly; | |
541 | } | |
542 | ||
543 | /** | |
544 | * Collects validation Details. | |
545 | * | |
546 | * @param ast a {@code DetailAST} value | |
547 | * @return object containing all details to make a validation | |
548 | */ | |
549 | private static Details getDetails(DetailAST ast) { | |
550 | final Details details; | |
551 | switch (ast.getType()) { | |
552 | case TokenTypes.LITERAL_TRY: | |
553 | case TokenTypes.LITERAL_CATCH: | |
554 | case TokenTypes.LITERAL_FINALLY: | |
555 | details = getDetailsForTryCatchFinally(ast); | |
556 | break; | |
557 | case TokenTypes.LITERAL_IF: | |
558 | case TokenTypes.LITERAL_ELSE: | |
559 | details = getDetailsForIfElse(ast); | |
560 | break; | |
561 | case TokenTypes.LITERAL_DO: | |
562 | case TokenTypes.LITERAL_WHILE: | |
563 | case TokenTypes.LITERAL_FOR: | |
564 | details = getDetailsForLoops(ast); | |
565 | break; | |
566 | default: | |
567 | details = getDetailsForOthers(ast); | |
568 | break; | |
569 | } | |
570 | return details; | |
571 | } | |
572 | ||
573 | /** | |
574 | * Collects validation details for LITERAL_TRY, LITERAL_CATCH, and LITERAL_FINALLY. | |
575 | * | |
576 | * @param ast a {@code DetailAST} value | |
577 | * @return object containing all details to make a validation | |
578 | */ | |
579 | private static Details getDetailsForTryCatchFinally(DetailAST ast) { | |
580 | final DetailAST lcurly; | |
581 | DetailAST nextToken; | |
582 | final int tokenType = ast.getType(); | |
583 | if (tokenType == TokenTypes.LITERAL_TRY) { | |
584 | if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) { | |
585 | lcurly = ast.getFirstChild().getNextSibling(); | |
586 | } | |
587 | else { | |
588 | lcurly = ast.getFirstChild(); | |
589 | } | |
590 | nextToken = lcurly.getNextSibling(); | |
591 | } | |
592 | else { | |
593 | nextToken = ast.getNextSibling(); | |
594 | lcurly = ast.getLastChild(); | |
595 | } | |
596 | ||
597 | final boolean shouldCheckLastRcurly; | |
598 | if (nextToken == null) { | |
599 | shouldCheckLastRcurly = true; | |
600 | nextToken = getNextToken(ast); | |
601 | } | |
602 | else { | |
603 | shouldCheckLastRcurly = false; | |
604 | } | |
605 | ||
606 | final DetailAST rcurly = lcurly.getLastChild(); | |
607 | return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly); | |
608 | } | |
609 | ||
610 | /** | |
611 | * Collects validation details for LITERAL_IF and LITERAL_ELSE. | |
612 | * | |
613 | * @param ast a {@code DetailAST} value | |
614 | * @return object containing all details to make a validation | |
615 | */ | |
616 | private static Details getDetailsForIfElse(DetailAST ast) { | |
617 | final boolean shouldCheckLastRcurly; | |
618 | final DetailAST lcurly; | |
619 | DetailAST nextToken = ast.findFirstToken(TokenTypes.LITERAL_ELSE); | |
620 | ||
621 | if (nextToken == null) { | |
622 | shouldCheckLastRcurly = true; | |
623 | nextToken = getNextToken(ast); | |
624 | lcurly = ast.getLastChild(); | |
625 | } | |
626 | else { | |
627 | shouldCheckLastRcurly = false; | |
628 | lcurly = nextToken.getPreviousSibling(); | |
629 | } | |
630 | ||
631 | DetailAST rcurly = null; | |
632 | if (lcurly.getType() == TokenTypes.SLIST) { | |
633 | rcurly = lcurly.getLastChild(); | |
634 | } | |
635 | return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly); | |
636 | } | |
637 | ||
638 | /** | |
639 | * Collects validation details for CLASS_DEF, RECORD_DEF, METHOD DEF, CTOR_DEF, STATIC_INIT, | |
640 | * INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, and COMPACT_CTOR_DEF. | |
641 | * | |
642 | * @param ast a {@code DetailAST} value | |
643 | * @return an object containing all details to make a validation | |
644 | */ | |
645 | private static Details getDetailsForOthers(DetailAST ast) { | |
646 | DetailAST rcurly = null; | |
647 | final DetailAST lcurly; | |
648 | final int tokenType = ast.getType(); | |
649 | if (isTokenWithNoChildSlist(tokenType)) { | |
650 | final DetailAST child = ast.getLastChild(); | |
651 | lcurly = child.getFirstChild(); | |
652 | rcurly = child.getLastChild(); | |
653 | } | |
654 | else { | |
655 | lcurly = ast.findFirstToken(TokenTypes.SLIST); | |
656 | if (lcurly != null) { | |
657 | // SLIST could be absent if method is abstract | |
658 | rcurly = lcurly.getLastChild(); | |
659 | } | |
660 | } | |
661 | return new Details(lcurly, rcurly, getNextToken(ast), true); | |
662 | } | |
663 | ||
664 | /** | |
665 | * Tests whether the provided tokenType will never have a SLIST as child in its AST. | |
666 | * Like CLASS_DEF, ANNOTATION_DEF etc. | |
667 | * | |
668 | * @param tokenType the tokenType to test against. | |
669 | * @return weather provided tokenType is definition token. | |
670 | */ | |
671 | private static boolean isTokenWithNoChildSlist(int tokenType) { | |
672 | return Arrays.stream(TOKENS_WITH_NO_CHILD_SLIST).anyMatch(token -> token == tokenType); | |
673 | } | |
674 | ||
675 | /** | |
676 | * Collects validation details for loops' tokens. | |
677 | * | |
678 | * @param ast a {@code DetailAST} value | |
679 | * @return an object containing all details to make a validation | |
680 | */ | |
681 | private static Details getDetailsForLoops(DetailAST ast) { | |
682 | DetailAST rcurly = null; | |
683 | final DetailAST lcurly; | |
684 | final DetailAST nextToken; | |
685 | final int tokenType = ast.getType(); | |
686 | final boolean shouldCheckLastRcurly; | |
687 | if (tokenType == TokenTypes.LITERAL_DO) { | |
688 | shouldCheckLastRcurly = false; | |
689 | nextToken = ast.findFirstToken(TokenTypes.DO_WHILE); | |
690 | lcurly = ast.findFirstToken(TokenTypes.SLIST); | |
691 | if (lcurly != null) { | |
692 | rcurly = lcurly.getLastChild(); | |
693 | } | |
694 | } | |
695 | else { | |
696 | shouldCheckLastRcurly = true; | |
697 | lcurly = ast.findFirstToken(TokenTypes.SLIST); | |
698 | if (lcurly != null) { | |
699 | // SLIST could be absent in code like "while(true);" | |
700 | rcurly = lcurly.getLastChild(); | |
701 | } | |
702 | nextToken = getNextToken(ast); | |
703 | } | |
704 | return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly); | |
705 | } | |
706 | ||
707 | /** | |
708 | * Finds next token after the given one. | |
709 | * | |
710 | * @param ast the given node. | |
711 | * @return the token which represents next lexical item. | |
712 | */ | |
713 | private static DetailAST getNextToken(DetailAST ast) { | |
714 | DetailAST next = null; | |
715 | DetailAST parent = ast; | |
716 | while (next == null && parent != null) { | |
717 | next = parent.getNextSibling(); | |
718 | parent = parent.getParent(); | |
719 | } | |
720 | return next; | |
721 | } | |
722 | } | |
723 | } | |
Mutations | ||
311 |
1.1 |
|
331 |
1.1 |