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