001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2022 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.LinkedList;
024import java.util.List;
025import java.util.Optional;
026
027import com.puppycrawl.tools.checkstyle.StatelessCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
029import com.puppycrawl.tools.checkstyle.api.DetailAST;
030import com.puppycrawl.tools.checkstyle.api.FileContents;
031import com.puppycrawl.tools.checkstyle.api.TokenTypes;
032import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
033import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
034import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
035import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
036
037/**
038 * <p>
039 * Checks for empty line separators before package, all import declarations,
040 * fields, constructors, methods, nested classes,
041 * static initializers and instance initializers.
042 * </p>
043 * <p>
044 * Checks for empty line separators before not only statements but
045 * implementation and documentation comments and blocks as well.
046 * </p>
047 * <p>
048 * ATTENTION: empty line separator is required between token siblings,
049 * not after line where token is found.
050 * If token does not have a sibling of the same type, then empty line
051 * is required at its end (for example for CLASS_DEF it is after '}').
052 * Also, trailing comments are skipped.
053 * </p>
054 * <p>
055 * ATTENTION: violations from multiple empty lines cannot be suppressed via XPath:
056 * <a href="https://github.com/checkstyle/checkstyle/issues/8179">#8179</a>.
057 * </p>
058 * <ul>
059 * <li>
060 * Property {@code allowNoEmptyLineBetweenFields} - Allow no empty line between fields.
061 * Type is {@code boolean}.
062 * Default value is {@code false}.
063 * </li>
064 * <li>
065 * Property {@code allowMultipleEmptyLines} - Allow multiple empty lines between class members.
066 * Type is {@code boolean}.
067 * Default value is {@code true}.
068 * </li>
069 * <li>
070 * Property {@code allowMultipleEmptyLinesInsideClassMembers} - Allow multiple
071 * empty lines inside class members.
072 * Type is {@code boolean}.
073 * Default value is {@code true}.
074 * </li>
075 * <li>
076 * Property {@code tokens} - tokens to check
077 * Type is {@code java.lang.String[]}.
078 * Validation type is {@code tokenSet}.
079 * Default value is:
080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PACKAGE_DEF">
081 * PACKAGE_DEF</a>,
082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IMPORT">
083 * IMPORT</a>,
084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_IMPORT">
085 * STATIC_IMPORT</a>,
086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
087 * CLASS_DEF</a>,
088 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
089 * INTERFACE_DEF</a>,
090 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
091 * ENUM_DEF</a>,
092 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
093 * STATIC_INIT</a>,
094 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
095 * INSTANCE_INIT</a>,
096 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
097 * METHOD_DEF</a>,
098 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
099 * CTOR_DEF</a>,
100 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
101 * VARIABLE_DEF</a>,
102 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
103 * RECORD_DEF</a>,
104 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF">
105 * COMPACT_CTOR_DEF</a>.
106 * </li>
107 * </ul>
108 * <p>
109 * To configure the default check:
110 * </p>
111 * <pre>
112 * &lt;module name=&quot;EmptyLineSeparator&quot;/&gt;
113 * </pre>
114 * <p>
115 * Example of declarations without empty line separator:
116 * </p>
117 *
118 * <pre>
119 * ///////////////////////////////////////////////////
120 * //HEADER
121 * ///////////////////////////////////////////////////
122 * package com.whitespace; // violation, 'package' should be separated from previous line.
123 * import java.io.Serializable; // violation, 'import' should be separated from previous line.
124 * class Foo { // violation, 'CLASS_DEF' should be separated from previous line.
125 *   public static final int FOO_CONST = 1;
126 *   public void foo() {} // violation, 'METHOD_DEF' should be separated from previous line.
127 * }
128 * </pre>
129 *
130 * <p>
131 * Example of declarations with empty line separator
132 * that is expected by the Check by default:
133 * </p>
134 *
135 * <pre>
136 * ///////////////////////////////////////////////////
137 * //HEADER
138 * ///////////////////////////////////////////////////
139 *
140 * package com.puppycrawl.tools.checkstyle.whitespace;
141 *
142 * import java.io.Serializable;
143 *
144 * class Foo {
145 *   public static final int FOO_CONST = 1;
146 *
147 *   public void foo() {}
148 * }
149 * </pre>
150 * <p>
151 * To check empty line before
152 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
153 * VARIABLE_DEF</a> and
154 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
155 * METHOD_DEF</a>:
156 * </p>
157 *
158 * <pre>
159 * &lt;module name=&quot;EmptyLineSeparator&quot;&gt;
160 *   &lt;property name=&quot;tokens&quot; value=&quot;VARIABLE_DEF, METHOD_DEF&quot;/&gt;
161 * &lt;/module&gt;
162 * </pre>
163 *
164 * <p>
165 * To allow no empty line between fields:
166 * </p>
167 * <pre>
168 * &lt;module name="EmptyLineSeparator"&gt;
169 *   &lt;property name="allowNoEmptyLineBetweenFields" value="true"/&gt;
170 * &lt;/module&gt;
171 * </pre>
172 *
173 * <p>
174 * Example:
175 * </p>
176 *
177 * <pre>
178 * class Foo {
179 *   int field1; // ok
180 *   double field2; // ok
181 *   long field3, field4 = 10L, field5; // ok
182 * }
183 * </pre>
184 * <p>
185 * Example of declarations with multiple empty lines between class members (allowed by default):
186 * </p>
187 *
188 * <pre>
189 * ///////////////////////////////////////////////////
190 * //HEADER
191 * ///////////////////////////////////////////////////
192 *
193 *
194 * package com.puppycrawl.tools.checkstyle.whitespace;
195 *
196 *
197 *
198 * import java.io.Serializable;
199 *
200 *
201 * class Foo {
202 *   public static final int FOO_CONST = 1;
203 *
204 *
205 *
206 *   public void foo() {} // OK
207 * }
208 * </pre>
209 * <p>
210 * To disallow multiple empty lines between class members:
211 * </p>
212 * <pre>
213 * &lt;module name=&quot;EmptyLineSeparator&quot;&gt;
214 *   &lt;property name=&quot;allowMultipleEmptyLines&quot; value=&quot;false&quot;/&gt;
215 * &lt;/module&gt;
216 * </pre>
217 * <pre>
218 * ///////////////////////////////////////////////////
219 * //HEADER
220 * ///////////////////////////////////////////////////
221 *
222 *
223 * package com.checkstyle.whitespace; // violation, 'package' has more than 1 empty lines before.
224 *
225 *
226 * import java.io.Serializable; // violation, 'import' has more than 1 empty lines before.
227 *
228 *
229 * class Foo { // violation, 'CLASS_DEF' has more than 1 empty lines before.
230 *   public static final int FOO_CONST = 1;
231 *
232 *
233 *
234 *   public void foo() {} // violation, 'METHOD_DEF' has more than 1 empty lines before.
235 * }
236 * </pre>
237 *
238 * <p>
239 * To disallow multiple empty lines inside constructor, initialization block and method:
240 * </p>
241 * <pre>
242 * &lt;module name="EmptyLineSeparator"&gt;
243 *   &lt;property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/&gt;
244 * &lt;/module&gt;
245 * </pre>
246 *
247 * <p>
248 * The check is valid only for statements that have body:
249 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
250 * CLASS_DEF</a>,
251 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
252 * INTERFACE_DEF</a>,
253 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
254 * ENUM_DEF</a>,
255 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
256 * STATIC_INIT</a>,
257 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
258 * INSTANCE_INIT</a>,
259 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
260 * METHOD_DEF</a>,
261 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
262 * CTOR_DEF</a>.
263 * </p>
264 * <p>
265 * Example of declarations with multiple empty lines inside method:
266 * </p>
267 *
268 * <pre>
269 * ///////////////////////////////////////////////////
270 * //HEADER
271 * ///////////////////////////////////////////////////
272 *
273 * package com.puppycrawl.tools.checkstyle.whitespace;
274 *
275 * class Foo {
276 *
277 *   public void foo() {
278 *
279 *
280 *     System.out.println(1); // violation, There is more than 1 empty line one after another
281 *                            // in previous line.
282 *   }
283 * }
284 * </pre>
285 * <p>
286 * To disallow multiple empty lines between class members:
287 * </p>
288 *
289 * <pre>
290 * &lt;module name="EmptyLineSeparator"&gt;
291 *   &lt;property name="allowMultipleEmptyLines" value="false"/&gt;
292 * &lt;/module&gt;
293 * </pre>
294 * <p>Example:</p>
295 * <pre>
296 * package com.puppycrawl.tools.checkstyle.whitespace;
297 *
298 * class Test {
299 *     private int k;
300 *
301 *
302 *     private static void foo() {} // violation, 'METHOD_DEF' has more than 1 empty lines before.
303 *
304 * }
305 * </pre>
306 * <p>
307 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
308 * </p>
309 * <p>
310 * Violation Message Keys:
311 * </p>
312 * <ul>
313 * <li>
314 * {@code empty.line.separator}
315 * </li>
316 * <li>
317 * {@code empty.line.separator.multiple.lines}
318 * </li>
319 * <li>
320 * {@code empty.line.separator.multiple.lines.after}
321 * </li>
322 * <li>
323 * {@code empty.line.separator.multiple.lines.inside}
324 * </li>
325 * </ul>
326 *
327 * @since 5.8
328 */
329@StatelessCheck
330public class EmptyLineSeparatorCheck extends AbstractCheck {
331
332    /**
333     * A key is pointing to the warning message empty.line.separator in "messages.properties"
334     * file.
335     */
336    public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";
337
338    /**
339     * A key is pointing to the warning message empty.line.separator.multiple.lines
340     *  in "messages.properties"
341     * file.
342     */
343    public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";
344
345    /**
346     * A key is pointing to the warning message empty.line.separator.lines.after
347     * in "messages.properties" file.
348     */
349    public static final String MSG_MULTIPLE_LINES_AFTER =
350            "empty.line.separator.multiple.lines.after";
351
352    /**
353     * A key is pointing to the warning message empty.line.separator.multiple.lines.inside
354     * in "messages.properties" file.
355     */
356    public static final String MSG_MULTIPLE_LINES_INSIDE =
357            "empty.line.separator.multiple.lines.inside";
358
359    /** Allow no empty line between fields. */
360    private boolean allowNoEmptyLineBetweenFields;
361
362    /** Allow multiple empty lines between class members. */
363    private boolean allowMultipleEmptyLines = true;
364
365    /** Allow multiple empty lines inside class members. */
366    private boolean allowMultipleEmptyLinesInsideClassMembers = true;
367
368    /**
369     * Setter to allow no empty line between fields.
370     *
371     * @param allow
372     *        User's value.
373     */
374    public final void setAllowNoEmptyLineBetweenFields(boolean allow) {
375        allowNoEmptyLineBetweenFields = allow;
376    }
377
378    /**
379     * Setter to allow multiple empty lines between class members.
380     *
381     * @param allow User's value.
382     */
383    public void setAllowMultipleEmptyLines(boolean allow) {
384        allowMultipleEmptyLines = allow;
385    }
386
387    /**
388     * Setter to allow multiple empty lines inside class members.
389     *
390     * @param allow User's value.
391     */
392    public void setAllowMultipleEmptyLinesInsideClassMembers(boolean allow) {
393        allowMultipleEmptyLinesInsideClassMembers = allow;
394    }
395
396    @Override
397    public boolean isCommentNodesRequired() {
398        return true;
399    }
400
401    @Override
402    public int[] getDefaultTokens() {
403        return getAcceptableTokens();
404    }
405
406    @Override
407    public int[] getAcceptableTokens() {
408        return new int[] {
409            TokenTypes.PACKAGE_DEF,
410            TokenTypes.IMPORT,
411            TokenTypes.STATIC_IMPORT,
412            TokenTypes.CLASS_DEF,
413            TokenTypes.INTERFACE_DEF,
414            TokenTypes.ENUM_DEF,
415            TokenTypes.STATIC_INIT,
416            TokenTypes.INSTANCE_INIT,
417            TokenTypes.METHOD_DEF,
418            TokenTypes.CTOR_DEF,
419            TokenTypes.VARIABLE_DEF,
420            TokenTypes.RECORD_DEF,
421            TokenTypes.COMPACT_CTOR_DEF,
422        };
423    }
424
425    @Override
426    public int[] getRequiredTokens() {
427        return CommonUtil.EMPTY_INT_ARRAY;
428    }
429
430    @Override
431    public void visitToken(DetailAST ast) {
432        checkComments(ast);
433        if (hasMultipleLinesBefore(ast)) {
434            log(ast, MSG_MULTIPLE_LINES, ast.getText());
435        }
436        if (!allowMultipleEmptyLinesInsideClassMembers) {
437            processMultipleLinesInside(ast);
438        }
439        if (ast.getType() == TokenTypes.PACKAGE_DEF) {
440            checkCommentInModifiers(ast);
441        }
442        DetailAST nextToken = ast.getNextSibling();
443        while (nextToken != null && TokenUtil.isCommentType(nextToken.getType())) {
444            nextToken = nextToken.getNextSibling();
445        }
446        if (nextToken != null) {
447            checkToken(ast, nextToken);
448        }
449    }
450
451    /**
452     * Checks that token and next token are separated.
453     *
454     * @param ast token to validate
455     * @param nextToken next sibling of the token
456     */
457    private void checkToken(DetailAST ast, DetailAST nextToken) {
458        final int astType = ast.getType();
459        switch (astType) {
460            case TokenTypes.VARIABLE_DEF:
461                processVariableDef(ast, nextToken);
462                break;
463            case TokenTypes.IMPORT:
464            case TokenTypes.STATIC_IMPORT:
465                processImport(ast, nextToken);
466                break;
467            case TokenTypes.PACKAGE_DEF:
468                processPackage(ast, nextToken);
469                break;
470            default:
471                if (nextToken.getType() == TokenTypes.RCURLY) {
472                    if (hasNotAllowedTwoEmptyLinesBefore(nextToken)) {
473                        final DetailAST result = getLastElementBeforeEmptyLines(ast,
474                                nextToken.getLineNo());
475                        log(result, MSG_MULTIPLE_LINES_AFTER, result.getText());
476                    }
477                }
478                else if (!hasEmptyLineAfter(ast)) {
479                    log(nextToken, MSG_SHOULD_BE_SEPARATED,
480                        nextToken.getText());
481                }
482        }
483    }
484
485    /**
486     * Checks that packageDef token is separated from comment in modifiers.
487     *
488     * @param packageDef package def token
489     */
490    private void checkCommentInModifiers(DetailAST packageDef) {
491        final Optional<DetailAST> comment = findCommentUnder(packageDef);
492        if (comment.isPresent()) {
493            log(comment.get(), MSG_SHOULD_BE_SEPARATED, comment.get().getText());
494        }
495    }
496
497    /**
498     * Log violation in case there are multiple empty lines inside constructor,
499     * initialization block or method.
500     *
501     * @param ast the ast to check.
502     */
503    private void processMultipleLinesInside(DetailAST ast) {
504        final int astType = ast.getType();
505        if (isClassMemberBlock(astType)) {
506            final List<Integer> emptyLines = getEmptyLines(ast);
507            final List<Integer> emptyLinesToLog = getEmptyLinesToLog(emptyLines);
508            for (Integer lineNo : emptyLinesToLog) {
509                log(getLastElementBeforeEmptyLines(ast, lineNo), MSG_MULTIPLE_LINES_INSIDE);
510            }
511        }
512    }
513
514    /**
515     * Returns the element after which empty lines exist.
516     *
517     * @param ast the ast to check.
518     * @param line the empty line which gives violation.
519     * @return The DetailAST after which empty lines are present.
520     */
521    private static DetailAST getLastElementBeforeEmptyLines(DetailAST ast, int line) {
522        DetailAST result = ast;
523        if (ast.getFirstChild().getLineNo() <= line) {
524            result = ast.getFirstChild();
525            while (result.getNextSibling() != null
526                    && result.getNextSibling().getLineNo() <= line) {
527                result = result.getNextSibling();
528            }
529            if (result.hasChildren()) {
530                result = getLastElementBeforeEmptyLines(result, line);
531            }
532        }
533
534        if (result.getNextSibling() != null) {
535            final Optional<DetailAST> postFixNode = getPostFixNode(result.getNextSibling());
536            if (postFixNode.isPresent()) {
537                // A post fix AST will always have a sibling METHOD CALL
538                // METHOD CALL will at least have two children
539                // The first child is DOT in case of POSTFIX which have at least 2 children
540                // First child of DOT again puts us back to normal AST tree which will
541                // recurse down below from here
542                final DetailAST firstChildAfterPostFix = postFixNode.get();
543                result = getLastElementBeforeEmptyLines(firstChildAfterPostFix, line);
544            }
545        }
546        return result;
547    }
548
549    /**
550     * Gets postfix Node from AST if present.
551     *
552     * @param ast the AST used to get postfix Node.
553     * @return Optional postfix node.
554     */
555    private static Optional<DetailAST> getPostFixNode(DetailAST ast) {
556        Optional<DetailAST> result = Optional.empty();
557        if (ast.getType() == TokenTypes.EXPR
558            // EXPR always has at least one child
559            && ast.getFirstChild().getType() == TokenTypes.METHOD_CALL) {
560            // METHOD CALL always has at two least child
561            final DetailAST node = ast.getFirstChild().getFirstChild();
562            if (node.getType() == TokenTypes.DOT) {
563                result = Optional.of(node);
564            }
565        }
566        return result;
567    }
568
569    /**
570     * Whether the AST is a class member block.
571     *
572     * @param astType the AST to check.
573     * @return true if the AST is a class member block.
574     */
575    private static boolean isClassMemberBlock(int astType) {
576        return TokenUtil.isOfType(astType,
577            TokenTypes.STATIC_INIT, TokenTypes.INSTANCE_INIT, TokenTypes.METHOD_DEF,
578            TokenTypes.CTOR_DEF, TokenTypes.COMPACT_CTOR_DEF);
579    }
580
581    /**
582     * Get list of empty lines.
583     *
584     * @param ast the ast to check.
585     * @return list of line numbers for empty lines.
586     */
587    // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166
588    @SuppressWarnings("deprecation")
589    private List<Integer> getEmptyLines(DetailAST ast) {
590        final DetailAST lastToken = ast.getLastChild().getLastChild();
591        int lastTokenLineNo = 0;
592        if (lastToken != null) {
593            // -1 as count starts from 0
594            // -2 as last token line cannot be empty, because it is a RCURLY
595            lastTokenLineNo = lastToken.getLineNo() - 2;
596        }
597        final List<Integer> emptyLines = new ArrayList<>();
598        final FileContents fileContents = getFileContents();
599
600        for (int lineNo = ast.getLineNo(); lineNo <= lastTokenLineNo; lineNo++) {
601            if (fileContents.lineIsBlank(lineNo)) {
602                emptyLines.add(lineNo);
603            }
604        }
605        return emptyLines;
606    }
607
608    /**
609     * Get list of empty lines to log.
610     *
611     * @param emptyLines list of empty lines.
612     * @return list of empty lines to log.
613     */
614    private static List<Integer> getEmptyLinesToLog(List<Integer> emptyLines) {
615        final List<Integer> emptyLinesToLog = new ArrayList<>();
616        if (emptyLines.size() >= 2) {
617            int previousEmptyLineNo = emptyLines.get(0);
618            for (int emptyLineNo : emptyLines) {
619                if (previousEmptyLineNo + 1 == emptyLineNo) {
620                    emptyLinesToLog.add(previousEmptyLineNo);
621                }
622                previousEmptyLineNo = emptyLineNo;
623            }
624        }
625        return emptyLinesToLog;
626    }
627
628    /**
629     * Whether the token has not allowed multiple empty lines before.
630     *
631     * @param ast the ast to check.
632     * @return true if the token has not allowed multiple empty lines before.
633     */
634    private boolean hasMultipleLinesBefore(DetailAST ast) {
635        return (ast.getType() != TokenTypes.VARIABLE_DEF || isTypeField(ast))
636                && hasNotAllowedTwoEmptyLinesBefore(ast);
637    }
638
639    /**
640     * Process Package.
641     *
642     * @param ast token
643     * @param nextToken next token
644     */
645    private void processPackage(DetailAST ast, DetailAST nextToken) {
646        if (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)) {
647            if (CheckUtil.isPackageInfo(getFilePath())) {
648                if (!ast.getFirstChild().hasChildren() && !isPrecededByJavadoc(ast)) {
649                    log(ast, MSG_SHOULD_BE_SEPARATED, ast.getText());
650                }
651            }
652            else {
653                log(ast, MSG_SHOULD_BE_SEPARATED, ast.getText());
654            }
655        }
656        if (isLineEmptyAfterPackage(ast)) {
657            final DetailAST elementAst = getViolationAstForPackage(ast);
658            log(elementAst, MSG_SHOULD_BE_SEPARATED, elementAst.getText());
659        }
660        else if (!hasEmptyLineAfter(ast)) {
661            log(nextToken, MSG_SHOULD_BE_SEPARATED, nextToken.getText());
662        }
663    }
664
665    /**
666     * Checks if there is another element at next line of package declaration.
667     *
668     * @param ast Package ast.
669     * @return true, if there is an element.
670     */
671    private static boolean isLineEmptyAfterPackage(DetailAST ast) {
672        DetailAST nextElement = ast.getNextSibling();
673        final int lastChildLineNo = ast.getLastChild().getLineNo();
674        while (nextElement.getLineNo() < lastChildLineNo + 1
675                && nextElement.getNextSibling() != null) {
676            nextElement = nextElement.getNextSibling();
677        }
678        return nextElement.getLineNo() == lastChildLineNo + 1;
679    }
680
681    /**
682     * Gets the Ast on which violation is to be given for package declaration.
683     *
684     * @param ast Package ast.
685     * @return Violation ast.
686     */
687    private static DetailAST getViolationAstForPackage(DetailAST ast) {
688        DetailAST nextElement = ast.getNextSibling();
689        final int lastChildLineNo = ast.getLastChild().getLineNo();
690        while (nextElement.getLineNo() < lastChildLineNo + 1) {
691            nextElement = nextElement.getNextSibling();
692        }
693        return nextElement;
694    }
695
696    /**
697     * Process Import.
698     *
699     * @param ast token
700     * @param nextToken next token
701     */
702    private void processImport(DetailAST ast, DetailAST nextToken) {
703        if (!TokenUtil.isOfType(nextToken, TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT)
704            && !hasEmptyLineAfter(ast)) {
705            log(nextToken, MSG_SHOULD_BE_SEPARATED, nextToken.getText());
706        }
707    }
708
709    /**
710     * Process Variable.
711     *
712     * @param ast token
713     * @param nextToken next Token
714     */
715    private void processVariableDef(DetailAST ast, DetailAST nextToken) {
716        if (isTypeField(ast) && !hasEmptyLineAfter(ast)
717                && isViolatingEmptyLineBetweenFieldsPolicy(nextToken)) {
718            log(nextToken, MSG_SHOULD_BE_SEPARATED,
719                    nextToken.getText());
720        }
721    }
722
723    /**
724     * Checks whether token placement violates policy of empty line between fields.
725     *
726     * @param detailAST token to be analyzed
727     * @return true if policy is violated and warning should be raised; false otherwise
728     */
729    private boolean isViolatingEmptyLineBetweenFieldsPolicy(DetailAST detailAST) {
730        return detailAST.getType() != TokenTypes.RCURLY
731                && (!allowNoEmptyLineBetweenFields
732                    || !TokenUtil.isOfType(detailAST, TokenTypes.COMMA, TokenTypes.VARIABLE_DEF));
733    }
734
735    /**
736     * Checks if a token has empty two previous lines and multiple empty lines is not allowed.
737     *
738     * @param token DetailAST token
739     * @return true, if token has empty two lines before and allowMultipleEmptyLines is false
740     */
741    private boolean hasNotAllowedTwoEmptyLinesBefore(DetailAST token) {
742        return !allowMultipleEmptyLines && hasEmptyLineBefore(token)
743                && isPrePreviousLineEmpty(token);
744    }
745
746    /**
747     * Check if group of comments located right before token has more than one previous empty line.
748     *
749     * @param token DetailAST token
750     */
751    private void checkComments(DetailAST token) {
752        if (!allowMultipleEmptyLines) {
753            if (TokenUtil.isOfType(token,
754                TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT,
755                TokenTypes.STATIC_IMPORT, TokenTypes.STATIC_INIT)) {
756                DetailAST previousNode = token.getPreviousSibling();
757                while (isCommentInBeginningOfLine(previousNode)) {
758                    if (hasEmptyLineBefore(previousNode) && isPrePreviousLineEmpty(previousNode)) {
759                        log(previousNode, MSG_MULTIPLE_LINES, previousNode.getText());
760                    }
761                    previousNode = previousNode.getPreviousSibling();
762                }
763            }
764            else {
765                checkCommentsInsideToken(token);
766            }
767        }
768    }
769
770    /**
771     * Check if group of comments located at the start of token has more than one previous empty
772     * line.
773     *
774     * @param token DetailAST token
775     */
776    private void checkCommentsInsideToken(DetailAST token) {
777        final List<DetailAST> childNodes = new LinkedList<>();
778        DetailAST childNode = token.getLastChild();
779        while (childNode != null) {
780            if (childNode.getType() == TokenTypes.MODIFIERS) {
781                for (DetailAST node = token.getFirstChild().getLastChild();
782                         node != null;
783                         node = node.getPreviousSibling()) {
784                    if (isCommentInBeginningOfLine(node)) {
785                        childNodes.add(node);
786                    }
787                }
788            }
789            else if (isCommentInBeginningOfLine(childNode)) {
790                childNodes.add(childNode);
791            }
792            childNode = childNode.getPreviousSibling();
793        }
794        for (DetailAST node : childNodes) {
795            if (hasEmptyLineBefore(node) && isPrePreviousLineEmpty(node)) {
796                log(node, MSG_MULTIPLE_LINES, node.getText());
797            }
798        }
799    }
800
801    /**
802     * Checks if a token has empty pre-previous line.
803     *
804     * @param token DetailAST token.
805     * @return true, if token has empty lines before.
806     */
807    private boolean isPrePreviousLineEmpty(DetailAST token) {
808        boolean result = false;
809        final int lineNo = token.getLineNo();
810        // 3 is the number of the pre-previous line because the numbering starts from zero.
811        final int number = 3;
812        if (lineNo >= number) {
813            final String prePreviousLine = getLine(lineNo - number);
814            result = CommonUtil.isBlank(prePreviousLine);
815        }
816        return result;
817    }
818
819    /**
820     * Checks if token have empty line after.
821     *
822     * @param token token.
823     * @return true if token have empty line after.
824     */
825    private boolean hasEmptyLineAfter(DetailAST token) {
826        DetailAST lastToken = token.getLastChild().getLastChild();
827        if (lastToken == null) {
828            lastToken = token.getLastChild();
829        }
830        DetailAST nextToken = token.getNextSibling();
831        if (TokenUtil.isCommentType(nextToken.getType())) {
832            nextToken = nextToken.getNextSibling();
833        }
834        // Start of the next token
835        final int nextBegin = nextToken.getLineNo();
836        // End of current token.
837        final int currentEnd = lastToken.getLineNo();
838        return hasEmptyLine(currentEnd + 1, nextBegin - 1);
839    }
840
841    /**
842     * Finds comment in next sibling of given packageDef.
843     *
844     * @param packageDef token to check
845     * @return comment under the token
846     */
847    private static Optional<DetailAST> findCommentUnder(DetailAST packageDef) {
848        return Optional.ofNullable(packageDef.getNextSibling())
849            .map(sibling -> sibling.findFirstToken(TokenTypes.MODIFIERS))
850            .map(DetailAST::getFirstChild)
851            .filter(token -> TokenUtil.isCommentType(token.getType()))
852            .filter(comment -> comment.getLineNo() == packageDef.getLineNo() + 1);
853    }
854
855    /**
856     * Checks, whether there are empty lines within the specified line range. Line numbering is
857     * started from 1 for parameter values
858     *
859     * @param startLine number of the first line in the range
860     * @param endLine number of the second line in the range
861     * @return {@code true} if found any blank line within the range, {@code false}
862     *         otherwise
863     */
864    // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166
865    @SuppressWarnings("deprecation")
866    private boolean hasEmptyLine(int startLine, int endLine) {
867        // Initial value is false - blank line not found
868        boolean result = false;
869        final FileContents fileContents = getFileContents();
870        for (int line = startLine; line <= endLine; line++) {
871            // Check, if the line is blank. Lines are numbered from 0, so subtract 1
872            if (fileContents.lineIsBlank(line - 1)) {
873                result = true;
874                break;
875            }
876        }
877        return result;
878    }
879
880    /**
881     * Checks if a token has an empty line before.
882     *
883     * @param token token.
884     * @return true, if token have empty line before.
885     */
886    private boolean hasEmptyLineBefore(DetailAST token) {
887        boolean result = false;
888        final int lineNo = token.getLineNo();
889        if (lineNo != 1) {
890            // [lineNo - 2] is the number of the previous line as the numbering starts from zero.
891            final String lineBefore = getLine(lineNo - 2);
892            result = CommonUtil.isBlank(lineBefore);
893        }
894        return result;
895    }
896
897    /**
898     * Check if token is comment, which starting in beginning of line.
899     *
900     * @param comment comment token for check.
901     * @return true, if token is comment, which starting in beginning of line.
902     */
903    private boolean isCommentInBeginningOfLine(DetailAST comment) {
904        // comment.getLineNo() - 1 is the number of the previous line as the numbering starts
905        // from zero.
906        boolean result = false;
907        if (comment != null) {
908            final String lineWithComment = getLine(comment.getLineNo() - 1).trim();
909            result = lineWithComment.startsWith("//") || lineWithComment.startsWith("/*");
910        }
911        return result;
912    }
913
914    /**
915     * Check if token is preceded by javadoc comment.
916     *
917     * @param token token for check.
918     * @return true, if token is preceded by javadoc comment.
919     */
920    private static boolean isPrecededByJavadoc(DetailAST token) {
921        boolean result = false;
922        final DetailAST previous = token.getPreviousSibling();
923        if (previous.getType() == TokenTypes.BLOCK_COMMENT_BEGIN
924                && JavadocUtil.isJavadocComment(previous.getFirstChild().getText())) {
925            result = true;
926        }
927        return result;
928    }
929
930    /**
931     * If variable definition is a type field.
932     *
933     * @param variableDef variable definition.
934     * @return true variable definition is a type field.
935     */
936    private static boolean isTypeField(DetailAST variableDef) {
937        return TokenUtil.isTypeDeclaration(variableDef.getParent().getParent().getType());
938    }
939
940}