001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.blocks;
021
022import java.util.Arrays;
023import java.util.Locale;
024
025import com.puppycrawl.tools.checkstyle.StatelessCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
030import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
031
032/**
033 * <p>
034 * Checks the placement of right curly braces ({@code '}'}) for code blocks. This check supports
035 * if-else, try-catch-finally blocks, switch statements, while-loops, for-loops,
036 * method definitions, class definitions, constructor definitions,
037 * instance, static initialization blocks, annotation definitions and enum definitions.
038 * For right curly brace of expression blocks of arrays, lambdas and class instances
039 * please follow issue
040 * <a href="https://github.com/checkstyle/checkstyle/issues/5945">#5945</a>.
041 * For right curly brace of enum constant please follow issue
042 * <a href="https://github.com/checkstyle/checkstyle/issues/7519">#7519</a>.
043 * </p>
044 * <ul>
045 * <li>
046 * Property {@code option} - Specify the policy on placement of a right curly brace
047 * (<code>'}'</code>).
048 * Type is {@code com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyOption}.
049 * Default value is {@code same}.
050 * </li>
051 * <li>
052 * Property {@code tokens} - tokens to check
053 * Type is {@code java.lang.String[]}.
054 * Validation type is {@code tokenSet}.
055 * Default value is:
056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
057 * LITERAL_TRY</a>,
058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
059 * LITERAL_CATCH</a>,
060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
061 * LITERAL_FINALLY</a>,
062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
063 * LITERAL_IF</a>,
064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
065 * LITERAL_ELSE</a>.
066 * </li>
067 * </ul>
068 * <p>
069 * To configure the check:
070 * </p>
071 * <pre>
072 * &lt;module name="RightCurly"/&gt;
073 * </pre>
074 * <p>
075 * Example:
076 * </p>
077 * <pre>
078 * public class Test {
079 *
080 *   public void test() {
081 *
082 *     if (foo) {
083 *       bar();
084 *     }           // violation, right curly must be in the same line as the 'else' keyword
085 *     else {
086 *       bar();
087 *     }
088 *
089 *     if (foo) {
090 *       bar();
091 *     } else {     // OK
092 *       bar();
093 *     }
094 *
095 *     if (foo) { bar(); } int i = 0; // violation
096 *                   // ^^^ statement is not allowed on same line after curly right brace
097 *
098 *     if (foo) { bar(); }            // OK
099 *     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 * &lt;module name=&quot;RightCurly&quot;&gt;
126 *   &lt;property name=&quot;option&quot; value=&quot;alone&quot;/&gt;
127 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_ELSE, METHOD_DEF&quot;/&gt;
128 * &lt;/module&gt;
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} for
165 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html">
166 * Switch</a> Statements:
167 * </p>
168 * <pre>
169 * &lt;module name=&quot;RightCurly&quot;&gt;
170 *  &lt;property name=&quot;option&quot; value=&quot;alone&quot;/&gt;
171 *  &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_SWITCH&quot;/&gt;
172 * &lt;/module&gt;
173 * </pre>
174 * <pre>
175 * class Test {
176 *
177 *     public void method0() {
178 *         int mode = 0;
179 *         switch (mode) {
180 *             case 1:
181 *                 int x = 1;
182 *                 break;
183 *             default:
184 *                 x = 0;
185 *         } // ok, RightCurly is alone
186 *     }
187 *
188 *     public void method0() {
189 *         int mode = 0;
190 *         switch (mode) {
191 *             case 1:
192 *                 int x = 1;
193 *                 break;
194 *             default:
195 *                 x = 0; } // violation, RightCurly should be alone on a line
196 *     }
197 *
198 * }
199 * </pre>
200 * <p>
201 * To configure the check with policy {@code alone_or_singleline} for {@code if} and
202 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
203 * METHOD_DEF</a>
204 * tokens:
205 * </p>
206 * <pre>
207 * &lt;module name=&quot;RightCurly&quot;&gt;
208 *  &lt;property name=&quot;option&quot; value=&quot;alone_or_singleline&quot;/&gt;
209 *  &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_IF, METHOD_DEF&quot;/&gt;
210 * &lt;/module&gt;
211 * </pre>
212 * <p>
213 * Example:
214 * </p>
215 * <pre>
216 * public class Test {
217 *
218 *   public void test() {
219 *
220 *     if (foo) {
221 *       bar();
222 *     } else {        // violation, right curly must be alone on line
223 *       bar();
224 *     }
225 *
226 *     if (foo) {
227 *       bar();
228 *     }               // OK
229 *     else {
230 *       bar();
231 *     }
232 *
233 *     try {
234 *       bar();
235 *     } catch (Exception e) {        // OK because config did not set token LITERAL_TRY
236 *       bar();
237 *     }
238 *
239 *   }                                // OK
240 *
241 *   public void violate() { bar(); } // OK , because singleline
242 * }
243 * </pre>
244 * <p>
245 * To configure the check with policy {@code alone_or_singleline} for
246 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html">
247 * Switch</a>
248 * Statements:
249 * </p>
250 * <pre>
251 * &lt;module name=&quot;RightCurly&quot;&gt;
252 *  &lt;property name=&quot;option&quot; value=&quot;alone_or_singleline&quot;/&gt;
253 *  &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_SWITCH&quot;/&gt;
254 * &lt;/module&gt;
255 * </pre>
256 * <pre>
257 * class Test {
258 *
259 *     public void method0() {
260 *         int mode = 0;
261 *         switch (mode) {
262 *             case 1:
263 *                 int x = 1;
264 *                 break;
265 *             default:
266 *                 x = 0;
267 *         } // ok
268 *     }
269 *
270 *     public static void method7() {
271 *         int mode = 0;
272 *         int x;
273 *         switch (mode) { case 1: x = 5; } // ok, RightCurly is on the same line as LeftCurly
274 *     }
275 *
276 *     public void method() {
277 *         int mode = 0;
278 *         int x;
279 *         switch (mode) {
280 *             case 1:
281 *                 x = 1; } // violation, right curly should be alone on line
282 *         }
283 * }
284 * </pre>
285 * <p>
286 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
287 * </p>
288 * <p>
289 * Violation Message Keys:
290 * </p>
291 * <ul>
292 * <li>
293 * {@code line.alone}
294 * </li>
295 * <li>
296 * {@code line.break.before}
297 * </li>
298 * <li>
299 * {@code line.same}
300 * </li>
301 * </ul>
302 *
303 * @since 3.0
304 */
305@StatelessCheck
306public class RightCurlyCheck extends AbstractCheck {
307
308    /**
309     * A key is pointing to the warning message text in "messages.properties"
310     * file.
311     */
312    public static final String MSG_KEY_LINE_BREAK_BEFORE = "line.break.before";
313
314    /**
315     * A key is pointing to the warning message text in "messages.properties"
316     * file.
317     */
318    public static final String MSG_KEY_LINE_ALONE = "line.alone";
319
320    /**
321     * A key is pointing to the warning message text in "messages.properties"
322     * file.
323     */
324    public static final String MSG_KEY_LINE_SAME = "line.same";
325
326    /**
327     * Specify the policy on placement of a right curly brace (<code>'}'</code>).
328     */
329    private RightCurlyOption option = RightCurlyOption.SAME;
330
331    /**
332     * Setter to specify the policy on placement of a right curly brace (<code>'}'</code>).
333     *
334     * @param optionStr string to decode option from
335     * @throws IllegalArgumentException if unable to decode
336     */
337    public void setOption(String optionStr) {
338        option = RightCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
339    }
340
341    @Override
342    public int[] getDefaultTokens() {
343        return new int[] {
344            TokenTypes.LITERAL_TRY,
345            TokenTypes.LITERAL_CATCH,
346            TokenTypes.LITERAL_FINALLY,
347            TokenTypes.LITERAL_IF,
348            TokenTypes.LITERAL_ELSE,
349        };
350    }
351
352    @Override
353    public int[] getAcceptableTokens() {
354        return new int[] {
355            TokenTypes.LITERAL_TRY,
356            TokenTypes.LITERAL_CATCH,
357            TokenTypes.LITERAL_FINALLY,
358            TokenTypes.LITERAL_IF,
359            TokenTypes.LITERAL_ELSE,
360            TokenTypes.CLASS_DEF,
361            TokenTypes.METHOD_DEF,
362            TokenTypes.CTOR_DEF,
363            TokenTypes.LITERAL_FOR,
364            TokenTypes.LITERAL_WHILE,
365            TokenTypes.LITERAL_DO,
366            TokenTypes.STATIC_INIT,
367            TokenTypes.INSTANCE_INIT,
368            TokenTypes.ANNOTATION_DEF,
369            TokenTypes.ENUM_DEF,
370            TokenTypes.INTERFACE_DEF,
371            TokenTypes.RECORD_DEF,
372            TokenTypes.COMPACT_CTOR_DEF,
373            TokenTypes.LITERAL_SWITCH,
374        };
375    }
376
377    @Override
378    public int[] getRequiredTokens() {
379        return CommonUtil.EMPTY_INT_ARRAY;
380    }
381
382    @Override
383    public void visitToken(DetailAST ast) {
384        final Details details = Details.getDetails(ast);
385        final DetailAST rcurly = details.rcurly;
386
387        if (rcurly != null) {
388            final String violation = validate(details);
389            if (!violation.isEmpty()) {
390                log(rcurly, violation, "}", rcurly.getColumnNo() + 1);
391            }
392        }
393    }
394
395    /**
396     * Does general validation.
397     *
398     * @param details for validation.
399     * @return violation message or empty string
400     *     if there was no violation during validation.
401     */
402    private String validate(Details details) {
403        String violation = "";
404        if (shouldHaveLineBreakBefore(option, details)) {
405            violation = MSG_KEY_LINE_BREAK_BEFORE;
406        }
407        else if (shouldBeOnSameLine(option, details)) {
408            violation = MSG_KEY_LINE_SAME;
409        }
410        else if (shouldBeAloneOnLine(option, details, getLine(details.rcurly.getLineNo() - 1))) {
411            violation = MSG_KEY_LINE_ALONE;
412        }
413        return violation;
414    }
415
416    /**
417     * Checks whether a right curly should have a line break before.
418     *
419     * @param bracePolicy option for placing the right curly brace.
420     * @param details details for validation.
421     * @return true if a right curly should have a line break before.
422     */
423    private static boolean shouldHaveLineBreakBefore(RightCurlyOption bracePolicy,
424                                                     Details details) {
425        return bracePolicy == RightCurlyOption.SAME
426                && !hasLineBreakBefore(details.rcurly)
427                && !TokenUtil.areOnSameLine(details.lcurly, details.rcurly);
428    }
429
430    /**
431     * Checks that a right curly should be on the same line as the next statement.
432     *
433     * @param bracePolicy option for placing the right curly brace
434     * @param details Details for validation
435     * @return true if a right curly should be alone on a line.
436     */
437    private static boolean shouldBeOnSameLine(RightCurlyOption bracePolicy, Details details) {
438        return bracePolicy == RightCurlyOption.SAME
439                && !details.shouldCheckLastRcurly
440                && !TokenUtil.areOnSameLine(details.rcurly, details.nextToken);
441    }
442
443    /**
444     * Checks that a right curly should be alone on a line.
445     *
446     * @param bracePolicy option for placing the right curly brace
447     * @param details Details for validation
448     * @param targetSrcLine A string with contents of rcurly's line
449     * @return true if a right curly should be alone on a line.
450     */
451    private static boolean shouldBeAloneOnLine(RightCurlyOption bracePolicy,
452                                               Details details,
453                                               String targetSrcLine) {
454        return bracePolicy == RightCurlyOption.ALONE
455                    && shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
456                || (bracePolicy == RightCurlyOption.ALONE_OR_SINGLELINE
457                    || details.shouldCheckLastRcurly)
458                    && shouldBeAloneOnLineWithNotAloneOption(details, targetSrcLine);
459    }
460
461    /**
462     * Whether right curly should be alone on line when ALONE option is used.
463     *
464     * @param details details for validation.
465     * @param targetSrcLine A string with contents of rcurly's line
466     * @return true, if right curly should be alone on line when ALONE option is used.
467     */
468    private static boolean shouldBeAloneOnLineWithAloneOption(Details details,
469                                                              String targetSrcLine) {
470        return !isAloneOnLine(details, targetSrcLine);
471    }
472
473    /**
474     * Whether right curly should be alone on line when ALONE_OR_SINGLELINE or SAME option is used.
475     *
476     * @param details details for validation.
477     * @param targetSrcLine A string with contents of rcurly's line
478     * @return true, if right curly should be alone on line
479     *         when ALONE_OR_SINGLELINE or SAME option is used.
480     */
481    private static boolean shouldBeAloneOnLineWithNotAloneOption(Details details,
482                                                                 String targetSrcLine) {
483        return shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
484                && !isBlockAloneOnSingleLine(details);
485    }
486
487    /**
488     * Checks whether right curly is alone on a line.
489     *
490     * @param details for validation.
491     * @param targetSrcLine A string with contents of rcurly's line
492     * @return true if right curly is alone on a line.
493     */
494    private static boolean isAloneOnLine(Details details, String targetSrcLine) {
495        final DetailAST rcurly = details.rcurly;
496        final DetailAST nextToken = details.nextToken;
497        return (nextToken == null || !TokenUtil.areOnSameLine(rcurly, nextToken)
498            || skipDoubleBraceInstInit(details))
499            && CommonUtil.hasWhitespaceBefore(details.rcurly.getColumnNo(),
500               targetSrcLine);
501    }
502
503    /**
504     * This method determines if the double brace initialization should be skipped over by the
505     * check. Double brace initializations are treated differently. The corresponding inner
506     * rcurly is treated as if it was alone on line even when it may be followed by another
507     * rcurly and a semi, raising no violations.
508     * <i>Please do note though that the line should not contain anything other than the following
509     * right curly and the semi following it or else violations will be raised.</i>
510     * Only the kind of double brace initializations shown in the following example code will be
511     * skipped over:<br>
512     * <pre>
513     *     {@code Map<String, String> map = new LinkedHashMap<>() {{
514     *           put("alpha", "man");
515     *       }}; // no violation}
516     * </pre>
517     *
518     * @param details {@link Details} object containing the details relevant to the rcurly
519     * @return if the double brace initialization rcurly should be skipped over by the check
520     */
521    private static boolean skipDoubleBraceInstInit(Details details) {
522        boolean skipDoubleBraceInstInit = false;
523        final DetailAST tokenAfterNextToken = Details.getNextToken(details.nextToken);
524        if (tokenAfterNextToken != null) {
525            final DetailAST rcurly = details.rcurly;
526            skipDoubleBraceInstInit = rcurly.getParent().getParent()
527                    .getType() == TokenTypes.INSTANCE_INIT
528                    && details.nextToken.getType() == TokenTypes.RCURLY
529                    && !TokenUtil.areOnSameLine(rcurly, Details.getNextToken(tokenAfterNextToken));
530        }
531        return skipDoubleBraceInstInit;
532    }
533
534    /**
535     * Checks whether block has a single-line format and is alone on a line.
536     *
537     * @param details for validation.
538     * @return true if block has single-line format and is alone on a line.
539     */
540    private static boolean isBlockAloneOnSingleLine(Details details) {
541        DetailAST nextToken = details.nextToken;
542
543        while (nextToken != null && nextToken.getType() == TokenTypes.LITERAL_ELSE) {
544            nextToken = Details.getNextToken(nextToken);
545        }
546
547        if (nextToken != null && nextToken.getType() == TokenTypes.DO_WHILE) {
548            final DetailAST doWhileSemi = nextToken.getParent();
549            nextToken = Details.getNextToken(doWhileSemi);
550        }
551
552        return TokenUtil.areOnSameLine(details.lcurly, details.rcurly)
553            && (nextToken == null || !TokenUtil.areOnSameLine(details.rcurly, nextToken)
554                || isRightcurlyFollowedBySemicolon(details));
555    }
556
557    /**
558     * Checks whether the right curly is followed by a semicolon.
559     *
560     * @param details details for validation.
561     * @return true if the right curly is followed by a semicolon.
562     */
563    private static boolean isRightcurlyFollowedBySemicolon(Details details) {
564        return details.nextToken.getType() == TokenTypes.SEMI;
565    }
566
567    /**
568     * Checks if right curly has line break before.
569     *
570     * @param rightCurly right curly token.
571     * @return true, if right curly has line break before.
572     */
573    private static boolean hasLineBreakBefore(DetailAST rightCurly) {
574        DetailAST previousToken = rightCurly.getPreviousSibling();
575        if (previousToken == null) {
576            previousToken = rightCurly.getParent();
577        }
578        return !TokenUtil.areOnSameLine(rightCurly, previousToken);
579    }
580
581    /**
582     * Structure that contains all details for validation.
583     */
584    private static final class Details {
585
586        /**
587         * Token types that identify tokens that will never have SLIST in their AST.
588         */
589        private static final int[] TOKENS_WITH_NO_CHILD_SLIST = {
590            TokenTypes.CLASS_DEF,
591            TokenTypes.ENUM_DEF,
592            TokenTypes.ANNOTATION_DEF,
593            TokenTypes.INTERFACE_DEF,
594            TokenTypes.RECORD_DEF,
595        };
596
597        /** Right curly. */
598        private final DetailAST rcurly;
599        /** Left curly. */
600        private final DetailAST lcurly;
601        /** Next token. */
602        private final DetailAST nextToken;
603        /** Should check last right curly. */
604        private final boolean shouldCheckLastRcurly;
605
606        /**
607         * Constructor.
608         *
609         * @param lcurly the lcurly of the token whose details are being collected
610         * @param rcurly the rcurly of the token whose details are being collected
611         * @param nextToken the token after the token whose details are being collected
612         * @param shouldCheckLastRcurly boolean value to determine if to check last rcurly
613         */
614        private Details(DetailAST lcurly, DetailAST rcurly,
615                        DetailAST nextToken, boolean shouldCheckLastRcurly) {
616            this.lcurly = lcurly;
617            this.rcurly = rcurly;
618            this.nextToken = nextToken;
619            this.shouldCheckLastRcurly = shouldCheckLastRcurly;
620        }
621
622        /**
623         * Collects validation Details.
624         *
625         * @param ast a {@code DetailAST} value
626         * @return object containing all details to make a validation
627         */
628        private static Details getDetails(DetailAST ast) {
629            final Details details;
630            switch (ast.getType()) {
631                case TokenTypes.LITERAL_TRY:
632                case TokenTypes.LITERAL_CATCH:
633                    details = getDetailsForTryCatch(ast);
634                    break;
635                case TokenTypes.LITERAL_IF:
636                    details = getDetailsForIf(ast);
637                    break;
638                case TokenTypes.LITERAL_DO:
639                    details = getDetailsForDoLoops(ast);
640                    break;
641                case TokenTypes.LITERAL_SWITCH:
642                    details = getDetailsForSwitch(ast);
643                    break;
644                default:
645                    details = getDetailsForOthers(ast);
646                    break;
647            }
648            return details;
649        }
650
651        /**
652         * Collects details about switch statements and expressions.
653         *
654         * @param switchNode switch statement or expression to gather details about
655         * @return new Details about given switch statement or expression
656         */
657        private static Details getDetailsForSwitch(DetailAST switchNode) {
658            final DetailAST lcurly = switchNode.findFirstToken(TokenTypes.LCURLY);
659            final DetailAST rcurly;
660            DetailAST nextToken = null;
661            // skipping switch expression as check only handles statements
662            if (isSwitchExpression(switchNode)) {
663                rcurly = null;
664            }
665            else {
666                rcurly = switchNode.getLastChild();
667                nextToken = getNextToken(switchNode);
668            }
669            return new Details(lcurly, rcurly, nextToken, true);
670        }
671
672        /**
673         * Check whether switch is expression or not.
674         *
675         * @param switchNode switch statement or expression to provide detail
676         * @return true if it is a switch expression
677         */
678        private static boolean isSwitchExpression(DetailAST switchNode) {
679            DetailAST currentNode = switchNode;
680            boolean ans = false;
681
682            while (currentNode != null) {
683                if (currentNode.getType() == TokenTypes.EXPR) {
684                    ans = true;
685                }
686                currentNode = currentNode.getParent();
687            }
688            return ans;
689        }
690
691        /**
692         * Collects validation details for LITERAL_TRY, and LITERAL_CATCH.
693         *
694         * @param ast a {@code DetailAST} value
695         * @return object containing all details to make a validation
696         */
697        private static Details getDetailsForTryCatch(DetailAST ast) {
698            final DetailAST lcurly;
699            DetailAST nextToken;
700            final int tokenType = ast.getType();
701            if (tokenType == TokenTypes.LITERAL_TRY) {
702                if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
703                    lcurly = ast.getFirstChild().getNextSibling();
704                }
705                else {
706                    lcurly = ast.getFirstChild();
707                }
708                nextToken = lcurly.getNextSibling();
709            }
710            else {
711                nextToken = ast.getNextSibling();
712                lcurly = ast.getLastChild();
713            }
714
715            final boolean shouldCheckLastRcurly;
716            if (nextToken == null) {
717                shouldCheckLastRcurly = true;
718                nextToken = getNextToken(ast);
719            }
720            else {
721                shouldCheckLastRcurly = false;
722            }
723
724            final DetailAST rcurly = lcurly.getLastChild();
725            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
726        }
727
728        /**
729         * Collects validation details for LITERAL_IF.
730         *
731         * @param ast a {@code DetailAST} value
732         * @return object containing all details to make a validation
733         */
734        private static Details getDetailsForIf(DetailAST ast) {
735            final boolean shouldCheckLastRcurly;
736            final DetailAST lcurly;
737            DetailAST nextToken = ast.findFirstToken(TokenTypes.LITERAL_ELSE);
738
739            if (nextToken == null) {
740                shouldCheckLastRcurly = true;
741                nextToken = getNextToken(ast);
742                lcurly = ast.getLastChild();
743            }
744            else {
745                shouldCheckLastRcurly = false;
746                lcurly = nextToken.getPreviousSibling();
747            }
748
749            DetailAST rcurly = null;
750            if (lcurly.getType() == TokenTypes.SLIST) {
751                rcurly = lcurly.getLastChild();
752            }
753            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
754        }
755
756        /**
757         * Collects validation details for CLASS_DEF, RECORD_DEF, METHOD DEF, CTOR_DEF, STATIC_INIT,
758         * INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, and COMPACT_CTOR_DEF.
759         *
760         * @param ast a {@code DetailAST} value
761         * @return an object containing all details to make a validation
762         */
763        private static Details getDetailsForOthers(DetailAST ast) {
764            DetailAST rcurly = null;
765            final DetailAST lcurly;
766            final int tokenType = ast.getType();
767            if (isTokenWithNoChildSlist(tokenType)) {
768                final DetailAST child = ast.getLastChild();
769                lcurly = child;
770                rcurly = child.getLastChild();
771            }
772            else {
773                lcurly = ast.findFirstToken(TokenTypes.SLIST);
774                if (lcurly != null) {
775                    // SLIST could be absent if method is abstract
776                    rcurly = lcurly.getLastChild();
777                }
778            }
779            return new Details(lcurly, rcurly, getNextToken(ast), true);
780        }
781
782        /**
783         * Tests whether the provided tokenType will never have a SLIST as child in its AST.
784         * Like CLASS_DEF, ANNOTATION_DEF etc.
785         *
786         * @param tokenType the tokenType to test against.
787         * @return weather provided tokenType is definition token.
788         */
789        private static boolean isTokenWithNoChildSlist(int tokenType) {
790            return Arrays.stream(TOKENS_WITH_NO_CHILD_SLIST).anyMatch(token -> token == tokenType);
791        }
792
793        /**
794         * Collects validation details for LITERAL_DO loops' tokens.
795         *
796         * @param ast a {@code DetailAST} value
797         * @return an object containing all details to make a validation
798         */
799        private static Details getDetailsForDoLoops(DetailAST ast) {
800            final DetailAST lcurly = ast.findFirstToken(TokenTypes.SLIST);
801            final DetailAST nextToken = ast.findFirstToken(TokenTypes.DO_WHILE);
802            DetailAST rcurly = null;
803            if (lcurly != null) {
804                rcurly = lcurly.getLastChild();
805            }
806            return new Details(lcurly, rcurly, nextToken, false);
807        }
808
809        /**
810         * Finds next token after the given one.
811         *
812         * @param ast the given node.
813         * @return the token which represents next lexical item.
814         */
815        private static DetailAST getNextToken(DetailAST ast) {
816            DetailAST next = null;
817            DetailAST parent = ast;
818            while (next == null && parent != null) {
819                next = parent.getNextSibling();
820                parent = parent.getParent();
821            }
822            return next;
823        }
824    }
825}