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