View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.LinkedList;
24  import java.util.List;
25  import java.util.Optional;
26  
27  import com.puppycrawl.tools.checkstyle.StatelessCheck;
28  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
32  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
33  import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
34  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
35  
36  /**
37   * <div>
38   * Checks for empty line separators before package, all import declarations,
39   * fields, constructors, methods, nested classes,
40   * static initializers and instance initializers.
41   * </div>
42   *
43   * <p>
44   * Checks for empty line separators before not only statements but
45   * implementation and documentation comments and blocks as well.
46   * </p>
47   *
48   * <p>
49   * ATTENTION: empty line separator is required between token siblings,
50   * not after line where token is found.
51   * If token does not have a sibling of the same type, then empty line
52   * is required at its end (for example for CLASS_DEF it is after '}').
53   * Also, trailing comments are skipped.
54   * </p>
55   *
56   * @since 5.8
57   */
58  @StatelessCheck
59  public class EmptyLineSeparatorCheck extends AbstractCheck {
60  
61      /**
62       * A key is pointing to the warning message empty.line.separator in "messages.properties"
63       * file.
64       */
65      public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";
66  
67      /**
68       * A key is pointing to the warning message empty.line.separator.multiple.lines
69       *  in "messages.properties"
70       * file.
71       */
72      public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";
73  
74      /**
75       * A key is pointing to the warning message empty.line.separator.lines.after
76       * in "messages.properties" file.
77       */
78      public static final String MSG_MULTIPLE_LINES_AFTER =
79              "empty.line.separator.multiple.lines.after";
80  
81      /**
82       * A key is pointing to the warning message empty.line.separator.multiple.lines.inside
83       * in "messages.properties" file.
84       */
85      public static final String MSG_MULTIPLE_LINES_INSIDE =
86              "empty.line.separator.multiple.lines.inside";
87  
88      /** Allow no empty line between fields. */
89      private boolean allowNoEmptyLineBetweenFields;
90  
91      /** Allow multiple empty lines between class members. */
92      private boolean allowMultipleEmptyLines = true;
93  
94      /** Allow multiple empty lines inside class members. */
95      private boolean allowMultipleEmptyLinesInsideClassMembers = true;
96  
97      /**
98       * Setter to allow no empty line between fields.
99       *
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 }