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.whitespace;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.Optional;
027
028import com.puppycrawl.tools.checkstyle.StatelessCheck;
029import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
030import com.puppycrawl.tools.checkstyle.api.DetailAST;
031import com.puppycrawl.tools.checkstyle.api.FileContents;
032import com.puppycrawl.tools.checkstyle.api.TokenTypes;
033import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
034import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
035
036/**
037 * <p>
038 * Checks for empty line separators after header, package, all import declarations,
039 * fields, constructors, methods, nested classes,
040 * static initializers and instance initializers.
041 * </p>
042 * <p>
043 * ATTENTION: empty line separator is required between token siblings,
044 * not after line where token is found.
045 * If token does not have same type sibling then empty line
046 * is required at its end (for example for CLASS_DEF it is after '}').
047 * Also, trailing comments are skipped.
048 * </p>
049 * <ul>
050 * <li>
051 * Property {@code allowNoEmptyLineBetweenFields} - Allow no empty line between fields.
052 * Default value is {@code false}.
053 * </li>
054 * <li>
055 * Property {@code allowMultipleEmptyLines} - Allow multiple empty lines between class members.
056 * Default value is {@code true}.
057 * </li>
058 * <li>
059 * Property {@code allowMultipleEmptyLinesInsideClassMembers} - Allow multiple
060 * empty lines inside class members.
061 * Default value is {@code true}.
062 * </li>
063 * <li>
064 * Property {@code tokens} - tokens to check
065 * Default value is:
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PACKAGE_DEF">
067 * PACKAGE_DEF</a>,
068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IMPORT">
069 * IMPORT</a>,
070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_IMPORT">
071 * STATIC_IMPORT</a>,
072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
073 * CLASS_DEF</a>,
074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
075 * INTERFACE_DEF</a>,
076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
077 * ENUM_DEF</a>,
078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
079 * STATIC_INIT</a>,
080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
081 * INSTANCE_INIT</a>,
082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
083 * METHOD_DEF</a>,
084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
085 * CTOR_DEF</a>,
086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
087 * VARIABLE_DEF</a>.
088 * </li>
089 * </ul>
090 * <p>
091 * Example of declarations without empty line separator:
092 * </p>
093 *
094 * <pre>
095 * ///////////////////////////////////////////////////
096 * //HEADER
097 * ///////////////////////////////////////////////////
098 * package com.puppycrawl.tools.checkstyle.whitespace;
099 * import java.io.Serializable;
100 * class Foo {
101 *   public static final int FOO_CONST = 1;
102 *   public void foo() {} //should be separated from previous statement.
103 * }
104 * </pre>
105 *
106 * <p>
107 * To configure the check with default parameters:
108 * </p>
109 *
110 * <pre>
111 * &lt;module name=&quot;EmptyLineSeparator&quot;/&gt;
112 * </pre>
113 *
114 * <p>
115 * Example of declarations with empty line separator
116 * that is expected by the Check by default:
117 * </p>
118 *
119 * <pre>
120 * ///////////////////////////////////////////////////
121 * //HEADER
122 * ///////////////////////////////////////////////////
123 *
124 * package com.puppycrawl.tools.checkstyle.whitespace;
125 *
126 * import java.io.Serializable;
127 *
128 * class Foo {
129 *   public static final int FOO_CONST = 1;
130 *
131 *   public void foo() {}
132 * }
133 * </pre>
134 * <p>
135 * To check empty line after
136 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
137 * VARIABLE_DEF</a> and
138 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
139 * METHOD_DEF</a>:
140 * </p>
141 *
142 * <pre>
143 * &lt;module name=&quot;EmptyLineSeparator&quot;&gt;
144 *   &lt;property name=&quot;tokens&quot; value=&quot;VARIABLE_DEF, METHOD_DEF&quot;/&gt;
145 * &lt;/module&gt;
146 * </pre>
147 *
148 * <p>
149 * To allow no empty line between fields:
150 * </p>
151 * <pre>
152 * &lt;module name="EmptyLineSeparator"&gt;
153 *   &lt;property name="allowNoEmptyLineBetweenFields" value="true"/&gt;
154 * &lt;/module&gt;
155 * </pre>
156 *
157 * <p>
158 * Example of declarations with multiple empty lines between class members (allowed by default):
159 * </p>
160 *
161 * <pre>
162 * ///////////////////////////////////////////////////
163 * //HEADER
164 * ///////////////////////////////////////////////////
165 *
166 *
167 * package com.puppycrawl.tools.checkstyle.whitespace;
168 *
169 *
170 *
171 * import java.io.Serializable;
172 *
173 *
174 * class Foo {
175 *   public static final int FOO_CONST = 1;
176 *
177 *
178 *
179 *   public void foo() {} //should be separated from previous statement.
180 * }
181 * </pre>
182 * <p>
183 * To disallow multiple empty lines between class members:
184 * </p>
185 * <pre>
186 * &lt;module name=&quot;EmptyLineSeparator&quot;&gt;
187 *   &lt;property name=&quot;allowMultipleEmptyLines&quot; value=&quot;false&quot;/&gt;
188 * &lt;/module&gt;
189 * </pre>
190 *
191 * <p>
192 * To disallow multiple empty lines inside constructor, initialization block and method:
193 * </p>
194 * <pre>
195 * &lt;module name="EmptyLineSeparator"&gt;
196 *   &lt;property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/&gt;
197 * &lt;/module&gt;
198 * </pre>
199 *
200 * <p>
201 * The check is valid only for statements that have body:
202 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
203 * CLASS_DEF</a>,
204 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
205 * INTERFACE_DEF</a>,
206 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
207 * ENUM_DEF</a>,
208 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
209 * STATIC_INIT</a>,
210 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
211 * INSTANCE_INIT</a>,
212 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
213 * METHOD_DEF</a>,
214 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
215 * CTOR_DEF</a>.
216 * </p>
217 * <p>
218 * Example of declarations with multiple empty lines inside method:
219 * </p>
220 *
221 * <pre>
222 * ///////////////////////////////////////////////////
223 * //HEADER
224 * ///////////////////////////////////////////////////
225 *
226 * package com.puppycrawl.tools.checkstyle.whitespace;
227 *
228 * class Foo {
229 *
230 *   public void foo() {
231 *
232 *
233 *     System.out.println(1); // violation since method has 2 empty lines subsequently
234 *   }
235 * }
236 * </pre>
237 *
238 * @since 5.8
239 */
240@StatelessCheck
241public class EmptyLineSeparatorCheck extends AbstractCheck {
242
243    /**
244     * A key is pointing to the warning message empty.line.separator in "messages.properties"
245     * file.
246     */
247    public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";
248
249    /**
250     * A key is pointing to the warning message empty.line.separator.multiple.lines
251     *  in "messages.properties"
252     * file.
253     */
254    public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";
255
256    /**
257     * A key is pointing to the warning message empty.line.separator.lines.after
258     * in "messages.properties" file.
259     */
260    public static final String MSG_MULTIPLE_LINES_AFTER =
261            "empty.line.separator.multiple.lines.after";
262
263    /**
264     * A key is pointing to the warning message empty.line.separator.multiple.lines.inside
265     * in "messages.properties" file.
266     */
267    public static final String MSG_MULTIPLE_LINES_INSIDE =
268            "empty.line.separator.multiple.lines.inside";
269
270    /** List of AST token types, which can not have comment nodes to check inside. */
271    private static final List<Integer> TOKEN_TYPES_WITHOUT_COMMENTS_TO_CHECK_INSIDE =
272            Arrays.asList(TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT,
273                    TokenTypes.STATIC_INIT);
274
275    /** Allow no empty line between fields. */
276    private boolean allowNoEmptyLineBetweenFields;
277
278    /** Allow multiple empty lines between class members. */
279    private boolean allowMultipleEmptyLines = true;
280
281    /** Allow multiple empty lines inside class members. */
282    private boolean allowMultipleEmptyLinesInsideClassMembers = true;
283
284    /**
285     * Setter to allow no empty line between fields.
286     *
287     * @param allow
288     *        User's value.
289     */
290    public final void setAllowNoEmptyLineBetweenFields(boolean allow) {
291        allowNoEmptyLineBetweenFields = allow;
292    }
293
294    /**
295     * Setter to allow multiple empty lines between class members.
296     *
297     * @param allow User's value.
298     */
299    public void setAllowMultipleEmptyLines(boolean allow) {
300        allowMultipleEmptyLines = allow;
301    }
302
303    /**
304     * Setter to allow multiple empty lines inside class members.
305     *
306     * @param allow User's value.
307     */
308    public void setAllowMultipleEmptyLinesInsideClassMembers(boolean allow) {
309        allowMultipleEmptyLinesInsideClassMembers = allow;
310    }
311
312    @Override
313    public boolean isCommentNodesRequired() {
314        return true;
315    }
316
317    @Override
318    public int[] getDefaultTokens() {
319        return getAcceptableTokens();
320    }
321
322    @Override
323    public int[] getAcceptableTokens() {
324        return new int[] {
325            TokenTypes.PACKAGE_DEF,
326            TokenTypes.IMPORT,
327            TokenTypes.STATIC_IMPORT,
328            TokenTypes.CLASS_DEF,
329            TokenTypes.INTERFACE_DEF,
330            TokenTypes.ENUM_DEF,
331            TokenTypes.STATIC_INIT,
332            TokenTypes.INSTANCE_INIT,
333            TokenTypes.METHOD_DEF,
334            TokenTypes.CTOR_DEF,
335            TokenTypes.VARIABLE_DEF,
336        };
337    }
338
339    @Override
340    public int[] getRequiredTokens() {
341        return CommonUtil.EMPTY_INT_ARRAY;
342    }
343
344    @Override
345    public void visitToken(DetailAST ast) {
346        checkComments(ast);
347        if (hasMultipleLinesBefore(ast)) {
348            log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText());
349        }
350        if (!allowMultipleEmptyLinesInsideClassMembers) {
351            processMultipleLinesInside(ast);
352        }
353        if (ast.getType() == TokenTypes.PACKAGE_DEF) {
354            checkCommentInModifiers(ast);
355        }
356        DetailAST nextToken = ast.getNextSibling();
357        while (nextToken != null && isComment(nextToken)) {
358            nextToken = nextToken.getNextSibling();
359        }
360        if (nextToken != null) {
361            checkToken(ast, nextToken);
362        }
363    }
364
365    /**
366     * Checks that token and next token are separated.
367     *
368     * @param ast token to validate
369     * @param nextToken next sibling of the token
370     */
371    private void checkToken(DetailAST ast, DetailAST nextToken) {
372        final int astType = ast.getType();
373        switch (astType) {
374            case TokenTypes.VARIABLE_DEF:
375                processVariableDef(ast, nextToken);
376                break;
377            case TokenTypes.IMPORT:
378            case TokenTypes.STATIC_IMPORT:
379                processImport(ast, nextToken);
380                break;
381            case TokenTypes.PACKAGE_DEF:
382                processPackage(ast, nextToken);
383                break;
384            default:
385                if (nextToken.getType() == TokenTypes.RCURLY) {
386                    if (hasNotAllowedTwoEmptyLinesBefore(nextToken)) {
387                        log(ast.getLineNo(), MSG_MULTIPLE_LINES_AFTER, ast.getText());
388                    }
389                }
390                else if (!hasEmptyLineAfter(ast)) {
391                    log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED,
392                        nextToken.getText());
393                }
394        }
395    }
396
397    /**
398     * Checks that packageDef token is separated from comment in modifiers.
399     *
400     * @param packageDef package def token
401     */
402    private void checkCommentInModifiers(DetailAST packageDef) {
403        final Optional<DetailAST> comment = findCommentUnder(packageDef);
404        if (comment.isPresent()) {
405            log(comment.get().getLineNo(), MSG_SHOULD_BE_SEPARATED, comment.get().getText());
406        }
407    }
408
409    /**
410     * Log violation in case there are multiple empty lines inside constructor,
411     * initialization block or method.
412     *
413     * @param ast the ast to check.
414     */
415    private void processMultipleLinesInside(DetailAST ast) {
416        final int astType = ast.getType();
417        if (isClassMemberBlock(astType)) {
418            final List<Integer> emptyLines = getEmptyLines(ast);
419            final List<Integer> emptyLinesToLog = getEmptyLinesToLog(emptyLines);
420
421            for (Integer lineNo : emptyLinesToLog) {
422                // Checkstyle counts line numbers from 0 but IDE from 1
423                log(lineNo + 1, MSG_MULTIPLE_LINES_INSIDE);
424            }
425        }
426    }
427
428    /**
429     * Whether the AST is a class member block.
430     *
431     * @param astType the AST to check.
432     * @return true if the AST is a class member block.
433     */
434    private static boolean isClassMemberBlock(int astType) {
435        return astType == TokenTypes.STATIC_INIT
436                || astType == TokenTypes.INSTANCE_INIT
437                || astType == TokenTypes.METHOD_DEF
438                || astType == TokenTypes.CTOR_DEF;
439    }
440
441    /**
442     * Get list of empty lines.
443     *
444     * @param ast the ast to check.
445     * @return list of line numbers for empty lines.
446     */
447    private List<Integer> getEmptyLines(DetailAST ast) {
448        final DetailAST lastToken = ast.getLastChild().getLastChild();
449        int lastTokenLineNo = 0;
450        if (lastToken != null) {
451            // -1 as count starts from 0
452            // -2 as last token line cannot be empty, because it is a RCURLY
453            lastTokenLineNo = lastToken.getLineNo() - 2;
454        }
455        final List<Integer> emptyLines = new ArrayList<>();
456        final FileContents fileContents = getFileContents();
457
458        for (int lineNo = ast.getLineNo(); lineNo <= lastTokenLineNo; lineNo++) {
459            if (fileContents.lineIsBlank(lineNo)) {
460                emptyLines.add(lineNo);
461            }
462        }
463        return emptyLines;
464    }
465
466    /**
467     * Get list of empty lines to log.
468     *
469     * @param emptyLines list of empty lines.
470     * @return list of empty lines to log.
471     */
472    private static List<Integer> getEmptyLinesToLog(List<Integer> emptyLines) {
473        final List<Integer> emptyLinesToLog = new ArrayList<>();
474        if (emptyLines.size() >= 2) {
475            int previousEmptyLineNo = emptyLines.get(0);
476            for (int emptyLineNo : emptyLines) {
477                if (previousEmptyLineNo + 1 == emptyLineNo) {
478                    emptyLinesToLog.add(emptyLineNo);
479                }
480                previousEmptyLineNo = emptyLineNo;
481            }
482        }
483        return emptyLinesToLog;
484    }
485
486    /**
487     * Whether the token has not allowed multiple empty lines before.
488     *
489     * @param ast the ast to check.
490     * @return true if the token has not allowed multiple empty lines before.
491     */
492    private boolean hasMultipleLinesBefore(DetailAST ast) {
493        boolean result = false;
494        if ((ast.getType() != TokenTypes.VARIABLE_DEF
495            || isTypeField(ast))
496                && hasNotAllowedTwoEmptyLinesBefore(ast)) {
497            result = true;
498        }
499        return result;
500    }
501
502    /**
503     * Process Package.
504     *
505     * @param ast token
506     * @param nextToken next token
507     */
508    private void processPackage(DetailAST ast, DetailAST nextToken) {
509        if (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)) {
510            if (getFileContents().getFileName().endsWith("package-info.java")) {
511                if (!ast.getFirstChild().hasChildren() && !isPrecededByJavadoc(ast)) {
512                    log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText());
513                }
514            }
515            else {
516                log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText());
517            }
518        }
519        if (!hasEmptyLineAfter(ast)) {
520            log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText());
521        }
522    }
523
524    /**
525     * Process Import.
526     *
527     * @param ast token
528     * @param nextToken next token
529     */
530    private void processImport(DetailAST ast, DetailAST nextToken) {
531        if (nextToken.getType() != TokenTypes.IMPORT
532                && nextToken.getType() != TokenTypes.STATIC_IMPORT && !hasEmptyLineAfter(ast)) {
533            log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText());
534        }
535    }
536
537    /**
538     * Process Variable.
539     *
540     * @param ast token
541     * @param nextToken next Token
542     */
543    private void processVariableDef(DetailAST ast, DetailAST nextToken) {
544        if (isTypeField(ast) && !hasEmptyLineAfter(ast)
545                && isViolatingEmptyLineBetweenFieldsPolicy(nextToken)) {
546            log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED,
547                    nextToken.getText());
548        }
549    }
550
551    /**
552     * Checks whether token placement violates policy of empty line between fields.
553     *
554     * @param detailAST token to be analyzed
555     * @return true if policy is violated and warning should be raised; false otherwise
556     */
557    private boolean isViolatingEmptyLineBetweenFieldsPolicy(DetailAST detailAST) {
558        return detailAST.getType() != TokenTypes.RCURLY
559                && (!allowNoEmptyLineBetweenFields
560                    || detailAST.getType() != TokenTypes.VARIABLE_DEF);
561    }
562
563    /**
564     * Checks if a token has empty two previous lines and multiple empty lines is not allowed.
565     *
566     * @param token DetailAST token
567     * @return true, if token has empty two lines before and allowMultipleEmptyLines is false
568     */
569    private boolean hasNotAllowedTwoEmptyLinesBefore(DetailAST token) {
570        return !allowMultipleEmptyLines && hasEmptyLineBefore(token)
571                && isPrePreviousLineEmpty(token);
572    }
573
574    /**
575     * Check if group of comments located right before token has more than one previous empty line.
576     *
577     * @param token DetailAST token
578     */
579    private void checkComments(DetailAST token) {
580        if (!allowMultipleEmptyLines) {
581            if (TOKEN_TYPES_WITHOUT_COMMENTS_TO_CHECK_INSIDE.contains(token.getType())) {
582                DetailAST previousNode = token.getPreviousSibling();
583                while (isCommentInBeginningOfLine(previousNode)) {
584                    if (hasEmptyLineBefore(previousNode) && isPrePreviousLineEmpty(previousNode)) {
585                        log(previousNode, MSG_MULTIPLE_LINES, previousNode.getText());
586                    }
587                    previousNode = previousNode.getPreviousSibling();
588                }
589            }
590            else {
591                checkCommentsInsideToken(token);
592            }
593        }
594    }
595
596    /**
597     * Check if group of comments located at the start of token has more than one previous empty
598     * line.
599     *
600     * @param token DetailAST token
601     */
602    private void checkCommentsInsideToken(DetailAST token) {
603        final List<DetailAST> childNodes = new LinkedList<>();
604        DetailAST childNode = token.getLastChild();
605        while (childNode != null) {
606            if (childNode.getType() == TokenTypes.MODIFIERS) {
607                for (DetailAST node = token.getFirstChild().getLastChild();
608                         node != null;
609                         node = node.getPreviousSibling()) {
610                    if (isCommentInBeginningOfLine(node)) {
611                        childNodes.add(node);
612                    }
613                }
614            }
615            else if (isCommentInBeginningOfLine(childNode)) {
616                childNodes.add(childNode);
617            }
618            childNode = childNode.getPreviousSibling();
619        }
620        for (DetailAST node : childNodes) {
621            if (hasEmptyLineBefore(node) && isPrePreviousLineEmpty(node)) {
622                log(node, MSG_MULTIPLE_LINES, node.getText());
623            }
624        }
625    }
626
627    /**
628     * Checks if a token has empty pre-previous line.
629     *
630     * @param token DetailAST token.
631     * @return true, if token has empty lines before.
632     */
633    private boolean isPrePreviousLineEmpty(DetailAST token) {
634        boolean result = false;
635        final int lineNo = token.getLineNo();
636        // 3 is the number of the pre-previous line because the numbering starts from zero.
637        final int number = 3;
638        if (lineNo >= number) {
639            final String prePreviousLine = getLines()[lineNo - number];
640            result = CommonUtil.isBlank(prePreviousLine);
641        }
642        return result;
643    }
644
645    /**
646     * Checks if token have empty line after.
647     *
648     * @param token token.
649     * @return true if token have empty line after.
650     */
651    private boolean hasEmptyLineAfter(DetailAST token) {
652        DetailAST lastToken = token.getLastChild().getLastChild();
653        if (lastToken == null) {
654            lastToken = token.getLastChild();
655        }
656        DetailAST nextToken = token.getNextSibling();
657        if (isComment(nextToken)) {
658            nextToken = nextToken.getNextSibling();
659        }
660        // Start of the next token
661        final int nextBegin = nextToken.getLineNo();
662        // End of current token.
663        final int currentEnd = lastToken.getLineNo();
664        return hasEmptyLine(currentEnd + 1, nextBegin - 1);
665    }
666
667    /**
668     * Finds comment in next sibling of given packageDef.
669     *
670     * @param packageDef token to check
671     * @return comment under the token
672     */
673    private static Optional<DetailAST> findCommentUnder(DetailAST packageDef) {
674        return Optional.ofNullable(packageDef.getNextSibling())
675            .map(sibling -> sibling.findFirstToken(TokenTypes.MODIFIERS))
676            .map(DetailAST::getFirstChild)
677            .filter(EmptyLineSeparatorCheck::isComment)
678            .filter(comment -> comment.getLineNo() == packageDef.getLineNo() + 1);
679    }
680
681    /**
682     * Checks, whether there are empty lines within the specified line range. Line numbering is
683     * started from 1 for parameter values
684     *
685     * @param startLine number of the first line in the range
686     * @param endLine number of the second line in the range
687     * @return {@code true} if found any blank line within the range, {@code false}
688     *         otherwise
689     */
690    private boolean hasEmptyLine(int startLine, int endLine) {
691        // Initial value is false - blank line not found
692        boolean result = false;
693        final FileContents fileContents = getFileContents();
694        for (int line = startLine; line <= endLine; line++) {
695            // Check, if the line is blank. Lines are numbered from 0, so subtract 1
696            if (fileContents.lineIsBlank(line - 1)) {
697                result = true;
698                break;
699            }
700        }
701        return result;
702    }
703
704    /**
705     * Checks if a token has a empty line before.
706     *
707     * @param token token.
708     * @return true, if token have empty line before.
709     */
710    private boolean hasEmptyLineBefore(DetailAST token) {
711        boolean result = false;
712        final int lineNo = token.getLineNo();
713        if (lineNo != 1) {
714            // [lineNo - 2] is the number of the previous line as the numbering starts from zero.
715            final String lineBefore = getLines()[lineNo - 2];
716            result = CommonUtil.isBlank(lineBefore);
717        }
718        return result;
719    }
720
721    /**
722     * Check if token is comment, which starting in beginning of line.
723     *
724     * @param comment comment token for check.
725     * @return true, if token is comment, which starting in beginning of line.
726     */
727    private boolean isCommentInBeginningOfLine(DetailAST comment) {
728        // [comment.getLineNo() - 1] is the number of the previous line as the numbering starts
729        // from zero.
730        boolean result = false;
731        if (comment != null) {
732            final String lineWithComment = getLines()[comment.getLineNo() - 1].trim();
733            result = lineWithComment.startsWith("//") || lineWithComment.startsWith("/*");
734        }
735        return result;
736    }
737
738    /**
739     * Check if token is preceded by javadoc comment.
740     *
741     * @param token token for check.
742     * @return true, if token is preceded by javadoc comment.
743     */
744    private static boolean isPrecededByJavadoc(DetailAST token) {
745        boolean result = false;
746        final DetailAST previous = token.getPreviousSibling();
747        if (previous.getType() == TokenTypes.BLOCK_COMMENT_BEGIN
748                && JavadocUtil.isJavadocComment(previous.getFirstChild().getText())) {
749            result = true;
750        }
751        return result;
752    }
753
754    /**
755     * Check if token is a comment.
756     *
757     * @param ast ast node
758     * @return true, if given ast is comment.
759     */
760    private static boolean isComment(DetailAST ast) {
761        return ast.getType() == TokenTypes.SINGLE_LINE_COMMENT
762                   || ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN;
763    }
764
765    /**
766     * If variable definition is a type field.
767     *
768     * @param variableDef variable definition.
769     * @return true variable definition is a type field.
770     */
771    private static boolean isTypeField(DetailAST variableDef) {
772        final int parentType = variableDef.getParent().getParent().getType();
773        return parentType == TokenTypes.CLASS_DEF;
774    }
775
776}