View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.whitespace;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Optional;
25  
26  import com.puppycrawl.tools.checkstyle.StatelessCheck;
27  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
31  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32  import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
33  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
34  
35  /**
36   * <div>
37   * Checks for empty line separators after,
38   * fields, constructors, methods, nested classes,
39   * static initializers and instance initializers.
40   * </div>
41   *
42   * <p>
43   * For package declaration it checks for both before and after
44   * line separators and for import declarations it checks for
45   * empty line separator after last import declaration.
46   * </p>
47   *
48   * <p>
49   * Checks for empty line separators after not only statements but
50   * implementation and documentation comments and blocks as well.
51   * </p>
52   *
53   * <p>
54   * ATTENTION: empty line separator is required between token siblings,
55   * not after line where token is found.
56   * If token does not have a sibling of the same type, then empty line
57   * is required at its end (for example for CLASS_DEF it is after '}').
58   * Also, trailing comments are skipped.
59   * </p>
60   *
61   * @since 5.8
62   */
63  @StatelessCheck
64  public class EmptyLineSeparatorCheck extends AbstractCheck {
65  
66      /**
67       * A key is pointing to the warning message empty.line.separator in "messages.properties"
68       * file.
69       */
70      public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";
71  
72      /**
73       * A key is pointing to the warning message empty.line.separator.multiple.lines
74       *  in "messages.properties"
75       * file.
76       */
77      public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";
78  
79      /**
80       * A key is pointing to the warning message empty.line.separator.lines.after
81       * in "messages.properties" file.
82       */
83      public static final String MSG_MULTIPLE_LINES_AFTER =
84              "empty.line.separator.multiple.lines.after";
85  
86      /**
87       * A key is pointing to the warning message empty.line.separator.multiple.lines.inside
88       * in "messages.properties" file.
89       */
90      public static final String MSG_MULTIPLE_LINES_INSIDE =
91              "empty.line.separator.multiple.lines.inside";
92  
93      /** Allow no empty line between fields. */
94      private boolean allowNoEmptyLineBetweenFields;
95  
96      /** Allow multiple empty lines between class members. */
97      private boolean allowMultipleEmptyLines = true;
98  
99      /** 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 }