001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.DetailAstImpl;
026import com.puppycrawl.tools.checkstyle.StatelessCheck;
027import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.TokenTypes;
030import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
031import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
032import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
033
034/**
035 * <p>
036 * Checks the placement of right curly braces ({@code '}'}) for code blocks. This check supports
037 * if-else, try-catch-finally blocks, while-loops, for-loops,
038 * method definitions, class definitions, constructor definitions,
039 * instance, static initialization blocks, annotation definitions and enum definitions.
040 * For right curly brace of expression blocks of arrays, lambdas and class instances
041 * please follow issue
042 * <a href="https://github.com/checkstyle/checkstyle/issues/5945">#5945</a>.
043 * For right curly brace of enum constant please follow issue
044 * <a href="https://github.com/checkstyle/checkstyle/issues/7519">#7519</a>.
045 * </p>
046 * <ul>
047 * <li>
048 * Property {@code option} - Specify the policy on placement of a right curly brace
049 * (<code>'}'</code>).
050 * Type is {@code com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyOption}.
051 * Default value is {@code same}.
052 * </li>
053 * <li>
054 * Property {@code tokens} - tokens to check
055 * Type is {@code java.lang.String[]}.
056 * Validation type is {@code tokenSet}.
057 * Default value is:
058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
059 * LITERAL_TRY</a>,
060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
061 * LITERAL_CATCH</a>,
062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
063 * LITERAL_FINALLY</a>,
064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
065 * LITERAL_IF</a>,
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
067 * LITERAL_ELSE</a>.
068 * </li>
069 * </ul>
070 * <p>
071 * To configure the check:
072 * </p>
073 * <pre>
074 * &lt;module name="RightCurly"/&gt;
075 * </pre>
076 * <p>
077 * Example:
078 * </p>
079 * <pre>
080 * public class Test {
081 *
082 *   public void test() {
083 *
084 *     if (foo) {
085 *       bar();
086 *     }           // violation, right curly must be in the same line as the 'else' keyword
087 *     else {
088 *       bar();
089 *     }
090 *
091 *     if (foo) {
092 *       bar();
093 *     } else {     // OK
094 *       bar();
095 *     }
096 *
097 *     if (foo) { bar(); } int i = 0; // violation
098 *                   // ^^^ statement is not allowed on same line after curly right brace
099 *
100 *     if (foo) { bar(); }            // OK
101 *     int i = 0;
102 *
103 *     try {
104 *       bar();
105 *     }           // violation, rightCurly must be in the same line as 'catch' keyword
106 *     catch (Exception e) {
107 *       bar();
108 *     }
109 *
110 *     try {
111 *       bar();
112 *     } catch (Exception e) { // OK
113 *       bar();
114 *     }
115 *
116 *   }                         // OK
117 *
118 *   public void testSingleLine() { bar(); } // OK, because singleline is allowed
119 * }
120 * </pre>
121 * <p>
122 * To configure the check with policy {@code alone} for {@code else} and
123 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
124 * METHOD_DEF</a> tokens:
125 * </p>
126 * <pre>
127 * &lt;module name=&quot;RightCurly&quot;&gt;
128 *   &lt;property name=&quot;option&quot; value=&quot;alone&quot;/&gt;
129 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_ELSE, METHOD_DEF&quot;/&gt;
130 * &lt;/module&gt;
131 * </pre>
132 * <p>
133 * Example:
134 * </p>
135 * <pre>
136 * public class Test {
137 *
138 *   public void test() {
139 *
140 *     if (foo) {
141 *       bar();
142 *     } else { bar(); }   // violation, right curly must be alone on line
143 *
144 *     if (foo) {
145 *       bar();
146 *     } else {
147 *       bar();
148 *     }                   // OK
149 *
150 *     try {
151 *       bar();
152 *     } catch (Exception e) { // OK because config is set to token METHOD_DEF and LITERAL_ELSE
153 *       bar();
154 *     }
155 *
156 *   }                         // OK
157 *
158 *   public void violate() { bar; } // violation, singleline is not allowed here
159 *
160 *   public void ok() {
161 *     bar();
162 *   }                              // OK
163 * }
164 * </pre>
165 * <p>
166 * To configure the check with policy {@code alone_or_singleline} for {@code if}
167 * and <a href="apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
168 * METHOD_DEF</a>
169 * tokens:
170 * </p>
171 * <pre>
172 * &lt;module name=&quot;RightCurly&quot;&gt;
173 *  &lt;property name=&quot;option&quot; value=&quot;alone_or_singleline&quot;/&gt;
174 *  &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_IF, METHOD_DEF&quot;/&gt;
175 * &lt;/module&gt;
176 * </pre>
177 * <p>
178 * Example:
179 * </p>
180 * <pre>
181 * public class Test {
182 *
183 *   public void test() {
184 *
185 *     if (foo) {
186 *       bar();
187 *     } else {        // violation, right curly must be alone on line
188 *       bar();
189 *     }
190 *
191 *     if (foo) {
192 *       bar();
193 *     }               // OK
194 *     else {
195 *       bar();
196 *     }
197 *
198 *     try {
199 *       bar();
200 *     } catch (Exception e) {        // OK because config did not set token LITERAL_TRY
201 *       bar();
202 *     }
203 *
204 *   }                                // OK
205 *
206 *   public void violate() { bar(); } // OK , because singleline
207 * }
208 * </pre>
209 * <p>
210 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
211 * </p>
212 * <p>
213 * Violation Message Keys:
214 * </p>
215 * <ul>
216 * <li>
217 * {@code line.alone}
218 * </li>
219 * <li>
220 * {@code line.break.before}
221 * </li>
222 * <li>
223 * {@code line.same}
224 * </li>
225 * </ul>
226 *
227 * @since 3.0
228 */
229@StatelessCheck
230public class RightCurlyCheck extends AbstractCheck {
231
232    /**
233     * A key is pointing to the warning message text in "messages.properties"
234     * file.
235     */
236    public static final String MSG_KEY_LINE_BREAK_BEFORE = "line.break.before";
237
238    /**
239     * A key is pointing to the warning message text in "messages.properties"
240     * file.
241     */
242    public static final String MSG_KEY_LINE_ALONE = "line.alone";
243
244    /**
245     * A key is pointing to the warning message text in "messages.properties"
246     * file.
247     */
248    public static final String MSG_KEY_LINE_SAME = "line.same";
249
250    /**
251     * Specify the policy on placement of a right curly brace (<code>'}'</code>).
252     */
253    private RightCurlyOption option = RightCurlyOption.SAME;
254
255    /**
256     * Setter to specify the policy on placement of a right curly brace (<code>'}'</code>).
257     *
258     * @param optionStr string to decode option from
259     * @throws IllegalArgumentException if unable to decode
260     */
261    public void setOption(String optionStr) {
262        option = RightCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
263    }
264
265    @Override
266    public int[] getDefaultTokens() {
267        return new int[] {
268            TokenTypes.LITERAL_TRY,
269            TokenTypes.LITERAL_CATCH,
270            TokenTypes.LITERAL_FINALLY,
271            TokenTypes.LITERAL_IF,
272            TokenTypes.LITERAL_ELSE,
273        };
274    }
275
276    @Override
277    public int[] getAcceptableTokens() {
278        return new int[] {
279            TokenTypes.LITERAL_TRY,
280            TokenTypes.LITERAL_CATCH,
281            TokenTypes.LITERAL_FINALLY,
282            TokenTypes.LITERAL_IF,
283            TokenTypes.LITERAL_ELSE,
284            TokenTypes.CLASS_DEF,
285            TokenTypes.METHOD_DEF,
286            TokenTypes.CTOR_DEF,
287            TokenTypes.LITERAL_FOR,
288            TokenTypes.LITERAL_WHILE,
289            TokenTypes.LITERAL_DO,
290            TokenTypes.STATIC_INIT,
291            TokenTypes.INSTANCE_INIT,
292            TokenTypes.ANNOTATION_DEF,
293            TokenTypes.ENUM_DEF,
294            TokenTypes.INTERFACE_DEF,
295            TokenTypes.RECORD_DEF,
296            TokenTypes.COMPACT_CTOR_DEF,
297        };
298    }
299
300    @Override
301    public int[] getRequiredTokens() {
302        return CommonUtil.EMPTY_INT_ARRAY;
303    }
304
305    @Override
306    public void visitToken(DetailAST ast) {
307        final Details details = Details.getDetails(ast);
308        final DetailAST rcurly = details.rcurly;
309
310        if (rcurly != null) {
311            final String violation = validate(details);
312            if (!violation.isEmpty()) {
313                log(rcurly, violation, "}", rcurly.getColumnNo() + 1);
314            }
315        }
316    }
317
318    /**
319     * Does general validation.
320     *
321     * @param details for validation.
322     * @return violation message or empty string
323     *     if there was not violation during validation.
324     */
325    private String validate(Details details) {
326        String violation = "";
327        if (shouldHaveLineBreakBefore(option, details)) {
328            violation = MSG_KEY_LINE_BREAK_BEFORE;
329        }
330        else if (shouldBeOnSameLine(option, details)) {
331            violation = MSG_KEY_LINE_SAME;
332        }
333        else if (shouldBeAloneOnLine(option, details, getLine(details.rcurly.getLineNo() - 1))) {
334            violation = MSG_KEY_LINE_ALONE;
335        }
336        return violation;
337    }
338
339    /**
340     * Checks whether a right curly should have a line break before.
341     *
342     * @param bracePolicy option for placing the right curly brace.
343     * @param details details for validation.
344     * @return true if a right curly should have a line break before.
345     */
346    private static boolean shouldHaveLineBreakBefore(RightCurlyOption bracePolicy,
347                                                     Details details) {
348        return bracePolicy == RightCurlyOption.SAME
349                && !hasLineBreakBefore(details.rcurly)
350                && !TokenUtil.areOnSameLine(details.lcurly, details.rcurly);
351    }
352
353    /**
354     * Checks that a right curly should be on the same line as the next statement.
355     *
356     * @param bracePolicy option for placing the right curly brace
357     * @param details Details for validation
358     * @return true if a right curly should be alone on a line.
359     */
360    private static boolean shouldBeOnSameLine(RightCurlyOption bracePolicy, Details details) {
361        return bracePolicy == RightCurlyOption.SAME
362                && !details.shouldCheckLastRcurly
363                && !TokenUtil.areOnSameLine(details.rcurly, details.nextToken);
364    }
365
366    /**
367     * Checks that a right curly should be alone on a line.
368     *
369     * @param bracePolicy option for placing the right curly brace
370     * @param details Details for validation
371     * @param targetSrcLine A string with contents of rcurly's line
372     * @return true if a right curly should be alone on a line.
373     */
374    private static boolean shouldBeAloneOnLine(RightCurlyOption bracePolicy,
375                                               Details details,
376                                               String targetSrcLine) {
377        return bracePolicy == RightCurlyOption.ALONE
378                    && shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
379                || (bracePolicy == RightCurlyOption.ALONE_OR_SINGLELINE
380                    || details.shouldCheckLastRcurly)
381                    && shouldBeAloneOnLineWithNotAloneOption(details, targetSrcLine);
382    }
383
384    /**
385     * Whether right curly should be alone on line when ALONE option is used.
386     *
387     * @param details details for validation.
388     * @param targetSrcLine A string with contents of rcurly's line
389     * @return true, if right curly should be alone on line when ALONE option is used.
390     */
391    private static boolean shouldBeAloneOnLineWithAloneOption(Details details,
392                                                              String targetSrcLine) {
393        return !isAloneOnLine(details, targetSrcLine);
394    }
395
396    /**
397     * Whether right curly should be alone on line when ALONE_OR_SINGLELINE or SAME option is used.
398     *
399     * @param details details for validation.
400     * @param targetSrcLine A string with contents of rcurly's line
401     * @return true, if right curly should be alone on line
402     *         when ALONE_OR_SINGLELINE or SAME option is used.
403     */
404    private static boolean shouldBeAloneOnLineWithNotAloneOption(Details details,
405                                                                 String targetSrcLine) {
406        return shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
407                && !isBlockAloneOnSingleLine(details);
408    }
409
410    /**
411     * Checks whether right curly is alone on a line.
412     *
413     * @param details for validation.
414     * @param targetSrcLine A string with contents of rcurly's line
415     * @return true if right curly is alone on a line.
416     */
417    private static boolean isAloneOnLine(Details details, String targetSrcLine) {
418        final DetailAST rcurly = details.rcurly;
419        final DetailAST nextToken = details.nextToken;
420        return (!TokenUtil.areOnSameLine(rcurly, nextToken) || skipDoubleBraceInstInit(details))
421                && CommonUtil.hasWhitespaceBefore(details.rcurly.getColumnNo(), 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        final DetailAST rcurly = details.rcurly;
444        final DetailAST tokenAfterNextToken = Details.getNextToken(details.nextToken);
445        return rcurly.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
446                && details.nextToken.getType() == TokenTypes.RCURLY
447                && rcurly.getLineNo() != Details.getNextToken(tokenAfterNextToken).getLineNo();
448    }
449
450    /**
451     * Checks whether block has a single-line format and is alone on a line.
452     *
453     * @param details for validation.
454     * @return true if block has single-line format and is alone on a line.
455     */
456    private static boolean isBlockAloneOnSingleLine(Details details) {
457        final DetailAST rcurly = details.rcurly;
458        final DetailAST lcurly = details.lcurly;
459        DetailAST nextToken = details.nextToken;
460        while (nextToken.getType() == TokenTypes.LITERAL_ELSE) {
461            nextToken = Details.getNextToken(nextToken);
462        }
463        if (nextToken.getType() == TokenTypes.DO_WHILE) {
464            final DetailAST doWhileSemi = nextToken.getParent().getLastChild();
465            nextToken = Details.getNextToken(doWhileSemi);
466        }
467        return TokenUtil.areOnSameLine(rcurly, lcurly)
468                && (!TokenUtil.areOnSameLine(rcurly, nextToken)
469                || isRightcurlyFollowedBySemicolon(details));
470    }
471
472    /**
473     * Checks whether the right curly is followed by a semicolon.
474     *
475     * @param details details for validation.
476     * @return true if the right curly is followed by a semicolon.
477     */
478    private static boolean isRightcurlyFollowedBySemicolon(Details details) {
479        return details.nextToken.getType() == TokenTypes.SEMI;
480    }
481
482    /**
483     * Checks if right curly has line break before.
484     *
485     * @param rightCurly right curly token.
486     * @return true, if right curly has line break before.
487     */
488    private static boolean hasLineBreakBefore(DetailAST rightCurly) {
489        DetailAST previousToken = rightCurly.getPreviousSibling();
490        if (previousToken == null) {
491            previousToken = rightCurly.getParent();
492        }
493        return !TokenUtil.areOnSameLine(rightCurly, previousToken);
494    }
495
496    /**
497     * Structure that contains all details for validation.
498     */
499    private static final class Details {
500
501        /**
502         * Token types that identify tokens that will never have SLIST in their AST.
503         */
504        private static final int[] TOKENS_WITH_NO_CHILD_SLIST = {
505            TokenTypes.CLASS_DEF,
506            TokenTypes.ENUM_DEF,
507            TokenTypes.ANNOTATION_DEF,
508            TokenTypes.INTERFACE_DEF,
509            TokenTypes.RECORD_DEF,
510        };
511
512        /** Right curly. */
513        private final DetailAST rcurly;
514        /** Left curly. */
515        private final DetailAST lcurly;
516        /** Next token. */
517        private final DetailAST nextToken;
518        /** Should check last right curly. */
519        private final boolean shouldCheckLastRcurly;
520
521        /**
522         * Constructor.
523         *
524         * @param lcurly the lcurly of the token whose details are being collected
525         * @param rcurly the rcurly of the token whose details are being collected
526         * @param nextToken the token after the token whose details are being collected
527         * @param shouldCheckLastRcurly boolean value to determine if to check last rcurly
528         */
529        private Details(DetailAST lcurly, DetailAST rcurly,
530                        DetailAST nextToken, boolean shouldCheckLastRcurly) {
531            this.lcurly = lcurly;
532            this.rcurly = rcurly;
533            this.nextToken = nextToken;
534            this.shouldCheckLastRcurly = shouldCheckLastRcurly;
535        }
536
537        /**
538         * Collects validation Details.
539         *
540         * @param ast a {@code DetailAST} value
541         * @return object containing all details to make a validation
542         */
543        private static Details getDetails(DetailAST ast) {
544            final Details details;
545            switch (ast.getType()) {
546                case TokenTypes.LITERAL_TRY:
547                case TokenTypes.LITERAL_CATCH:
548                case TokenTypes.LITERAL_FINALLY:
549                    details = getDetailsForTryCatchFinally(ast);
550                    break;
551                case TokenTypes.LITERAL_IF:
552                case TokenTypes.LITERAL_ELSE:
553                    details = getDetailsForIfElse(ast);
554                    break;
555                case TokenTypes.LITERAL_DO:
556                case TokenTypes.LITERAL_WHILE:
557                case TokenTypes.LITERAL_FOR:
558                    details = getDetailsForLoops(ast);
559                    break;
560                default:
561                    details = getDetailsForOthers(ast);
562                    break;
563            }
564            return details;
565        }
566
567        /**
568         * Collects validation details for LITERAL_TRY, LITERAL_CATCH, and LITERAL_FINALLY.
569         *
570         * @param ast a {@code DetailAST} value
571         * @return object containing all details to make a validation
572         */
573        private static Details getDetailsForTryCatchFinally(DetailAST ast) {
574            final DetailAST lcurly;
575            DetailAST nextToken;
576            final int tokenType = ast.getType();
577            if (tokenType == TokenTypes.LITERAL_TRY) {
578                if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
579                    lcurly = ast.getFirstChild().getNextSibling();
580                }
581                else {
582                    lcurly = ast.getFirstChild();
583                }
584                nextToken = lcurly.getNextSibling();
585            }
586            else {
587                nextToken = ast.getNextSibling();
588                lcurly = ast.getLastChild();
589            }
590
591            final boolean shouldCheckLastRcurly;
592            if (nextToken == null) {
593                shouldCheckLastRcurly = true;
594                nextToken = getNextToken(ast);
595            }
596            else {
597                shouldCheckLastRcurly = false;
598            }
599
600            final DetailAST rcurly = lcurly.getLastChild();
601            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
602        }
603
604        /**
605         * Collects validation details for LITERAL_IF and LITERAL_ELSE.
606         *
607         * @param ast a {@code DetailAST} value
608         * @return object containing all details to make a validation
609         */
610        private static Details getDetailsForIfElse(DetailAST ast) {
611            final boolean shouldCheckLastRcurly;
612            final DetailAST lcurly;
613            DetailAST nextToken = ast.findFirstToken(TokenTypes.LITERAL_ELSE);
614
615            if (nextToken == null) {
616                shouldCheckLastRcurly = true;
617                nextToken = getNextToken(ast);
618                lcurly = ast.getLastChild();
619            }
620            else {
621                shouldCheckLastRcurly = false;
622                lcurly = nextToken.getPreviousSibling();
623            }
624
625            DetailAST rcurly = null;
626            if (lcurly.getType() == TokenTypes.SLIST) {
627                rcurly = lcurly.getLastChild();
628            }
629            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
630        }
631
632        /**
633         * Collects validation details for CLASS_DEF, RECORD_DEF, METHOD DEF, CTOR_DEF, STATIC_INIT,
634         * INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, and COMPACT_CTOR_DEF.
635         *
636         * @param ast a {@code DetailAST} value
637         * @return an object containing all details to make a validation
638         */
639        private static Details getDetailsForOthers(DetailAST ast) {
640            DetailAST rcurly = null;
641            final DetailAST lcurly;
642            final int tokenType = ast.getType();
643            if (isTokenWithNoChildSlist(tokenType)) {
644                final DetailAST child = ast.getLastChild();
645                lcurly = child.getFirstChild();
646                rcurly = child.getLastChild();
647            }
648            else {
649                lcurly = ast.findFirstToken(TokenTypes.SLIST);
650                if (lcurly != null) {
651                    // SLIST could be absent if method is abstract
652                    rcurly = lcurly.getLastChild();
653                }
654            }
655            return new Details(lcurly, rcurly, getNextToken(ast), true);
656        }
657
658        /**
659         * Tests whether the provided tokenType will never have a SLIST as child in its AST.
660         * Like CLASS_DEF, ANNOTATION_DEF etc.
661         *
662         * @param tokenType the tokenType to test against.
663         * @return weather provided tokenType is definition token.
664         */
665        private static boolean isTokenWithNoChildSlist(int tokenType) {
666            return Arrays.stream(TOKENS_WITH_NO_CHILD_SLIST).anyMatch(token -> token == tokenType);
667        }
668
669        /**
670         * Collects validation details for loops' tokens.
671         *
672         * @param ast a {@code DetailAST} value
673         * @return an object containing all details to make a validation
674         */
675        private static Details getDetailsForLoops(DetailAST ast) {
676            DetailAST rcurly = null;
677            final DetailAST lcurly;
678            final DetailAST nextToken;
679            final int tokenType = ast.getType();
680            final boolean shouldCheckLastRcurly;
681            if (tokenType == TokenTypes.LITERAL_DO) {
682                shouldCheckLastRcurly = false;
683                nextToken = ast.findFirstToken(TokenTypes.DO_WHILE);
684                lcurly = ast.findFirstToken(TokenTypes.SLIST);
685                if (lcurly != null) {
686                    rcurly = lcurly.getLastChild();
687                }
688            }
689            else {
690                shouldCheckLastRcurly = true;
691                lcurly = ast.findFirstToken(TokenTypes.SLIST);
692                if (lcurly != null) {
693                    // SLIST could be absent in code like "while(true);"
694                    rcurly = lcurly.getLastChild();
695                }
696                nextToken = getNextToken(ast);
697            }
698            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
699        }
700
701        /**
702         * Finds next token after the given one.
703         *
704         * @param ast the given node.
705         * @return the token which represents next lexical item.
706         */
707        private static DetailAST getNextToken(DetailAST ast) {
708            DetailAST next = null;
709            DetailAST parent = ast;
710            while (next == null && parent != null) {
711                next = parent.getNextSibling();
712                parent = parent.getParent();
713            }
714            if (next == null) {
715                // a DetailAST object with DetailAST#NOT_INITIALIZED for line and column numbers
716                // that no 'actual' DetailAST objects can have.
717                next = new DetailAstImpl();
718            }
719            else {
720                next = CheckUtil.getFirstNode(next);
721            }
722            return next;
723        }
724
725    }
726
727}