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