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