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