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 (nextToken != null) {
184             checkToken(ast, nextToken);
185         }
186     }
187 
188     /**
189      * Checks that token and next token are separated.
190      *
191      * @param ast token to validate
192      * @param nextToken next sibling of the token
193      */
194     private void checkToken(DetailAST ast, DetailAST nextToken) {
195         final int astType = ast.getType();
196 
197         switch (astType) {
198             case TokenTypes.VARIABLE_DEF -> processVariableDef(ast, nextToken);
199 
200             case TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT -> processImport(ast, nextToken);
201 
202             case TokenTypes.PACKAGE_DEF -> processPackage(ast, nextToken);
203 
204             default -> {
205                 if (nextToken.getType() == TokenTypes.RCURLY) {
206                     if (hasNotAllowedTwoEmptyLinesBefore(nextToken)) {
207                         final DetailAST result = getLastElementBeforeEmptyLines(
208                                 ast, nextToken.getLineNo()
209                         );
210                         log(result, MSG_MULTIPLE_LINES_AFTER, result.getText());
211                     }
212                 }
213                 else if (!hasEmptyLineAfter(ast)) {
214                     log(nextToken, MSG_SHOULD_BE_SEPARATED, nextToken.getText());
215                 }
216             }
217         }
218     }
219 
220     /**
221      * Checks that packageDef token is separated from comment in modifiers.
222      *
223      * @param packageDef package def token
224      */
225     private void checkCommentInModifiers(DetailAST packageDef) {
226         final Optional<DetailAST> comment = findCommentUnder(packageDef);
227         comment.ifPresent(commentValue -> {
228             log(commentValue, MSG_SHOULD_BE_SEPARATED, commentValue.getText());
229         });
230     }
231 
232     /**
233      * Log violation in case there are multiple empty lines inside constructor,
234      * initialization block or method.
235      *
236      * @param ast the ast to check.
237      */
238     private void processMultipleLinesInside(DetailAST ast) {
239         final int astType = ast.getType();
240         if (isClassMemberBlock(astType)) {
241             final List<Integer> emptyLines = getEmptyLines(ast);
242             final List<Integer> emptyLinesToLog = getEmptyLinesToLog(emptyLines);
243             for (Integer lineNo : emptyLinesToLog) {
244                 log(getLastElementBeforeEmptyLines(ast, lineNo), MSG_MULTIPLE_LINES_INSIDE);
245             }
246         }
247     }
248 
249     /**
250      * Returns the element after which empty lines exist.
251      *
252      * @param ast the ast to check.
253      * @param line the empty line which gives violation.
254      * @return The DetailAST after which empty lines are present.
255      */
256     private static DetailAST getLastElementBeforeEmptyLines(DetailAST ast, int line) {
257         DetailAST result = ast;
258         if (ast.getFirstChild().getLineNo() <= line) {
259             result = ast.getFirstChild();
260             while (result.getNextSibling() != null
261                     && result.getNextSibling().getLineNo() <= line) {
262                 result = result.getNextSibling();
263             }
264             if (result.hasChildren()) {
265                 result = getLastElementBeforeEmptyLines(result, line);
266             }
267         }
268 
269         if (result.getNextSibling() != null) {
270             final Optional<DetailAST> postFixNode = getPostFixNode(result.getNextSibling());
271             if (postFixNode.isPresent()) {
272                 // A post fix AST will always have a sibling METHOD CALL
273                 // METHOD CALL will at least have two children
274                 // The first child is DOT in case of POSTFIX which have at least 2 children
275                 // First child of DOT again puts us back to normal AST tree which will
276                 // recurse down below from here
277                 final DetailAST firstChildAfterPostFix = postFixNode.orElseThrow();
278                 result = getLastElementBeforeEmptyLines(firstChildAfterPostFix, line);
279             }
280         }
281         return result;
282     }
283 
284     /**
285      * Gets postfix Node from AST if present.
286      *
287      * @param ast the AST used to get postfix Node.
288      * @return Optional postfix node.
289      */
290     private static Optional<DetailAST> getPostFixNode(DetailAST ast) {
291         Optional<DetailAST> result = Optional.empty();
292         if (ast.getType() == TokenTypes.EXPR
293             // EXPR always has at least one child
294             && ast.getFirstChild().getType() == TokenTypes.METHOD_CALL) {
295             // METHOD CALL always has at two least child
296             final DetailAST node = ast.getFirstChild().getFirstChild();
297             if (node.getType() == TokenTypes.DOT) {
298                 result = Optional.of(node);
299             }
300         }
301         return result;
302     }
303 
304     /**
305      * Whether the AST is a class member block.
306      *
307      * @param astType the AST to check.
308      * @return true if the AST is a class member block.
309      */
310     private static boolean isClassMemberBlock(int astType) {
311         return TokenUtil.isOfType(astType,
312             TokenTypes.STATIC_INIT, TokenTypes.INSTANCE_INIT, TokenTypes.METHOD_DEF,
313             TokenTypes.CTOR_DEF, TokenTypes.COMPACT_CTOR_DEF);
314     }
315 
316     /**
317      * Get list of empty lines.
318      *
319      * @param ast the ast to check.
320      * @return list of line numbers for empty lines.
321      */
322     private List<Integer> getEmptyLines(DetailAST ast) {
323         final DetailAST lastToken = ast.getLastChild().getLastChild();
324         int lastTokenLineNo = 0;
325         if (lastToken != null) {
326             // -1 as count starts from 0
327             // -2 as last token line cannot be empty, because it is a RCURLY
328             lastTokenLineNo = lastToken.getLineNo() - 2;
329         }
330         final List<Integer> emptyLines = new ArrayList<>();
331 
332         for (int lineNo = ast.getLineNo(); lineNo <= lastTokenLineNo; lineNo++) {
333             if (CommonUtil.isBlank(getLine(lineNo))) {
334                 emptyLines.add(lineNo);
335             }
336         }
337         return emptyLines;
338     }
339 
340     /**
341      * Get list of empty lines to log.
342      *
343      * @param emptyLines list of empty lines.
344      * @return list of empty lines to log.
345      */
346     private static List<Integer> getEmptyLinesToLog(Iterable<Integer> emptyLines) {
347         final List<Integer> emptyLinesToLog = new ArrayList<>();
348         int previousEmptyLineNo = -1;
349         for (int emptyLineNo : emptyLines) {
350             if (previousEmptyLineNo + 1 == emptyLineNo) {
351                 emptyLinesToLog.add(previousEmptyLineNo);
352             }
353             previousEmptyLineNo = emptyLineNo;
354         }
355         return emptyLinesToLog;
356     }
357 
358     /**
359      * Whether the token has not allowed multiple empty lines before.
360      *
361      * @param ast the ast to check.
362      * @return true if the token has not allowed multiple empty lines before.
363      */
364     private boolean hasMultipleLinesBefore(DetailAST ast) {
365         return (ast.getType() != TokenTypes.VARIABLE_DEF || isTypeField(ast))
366                 && hasNotAllowedTwoEmptyLinesBefore(ast);
367     }
368 
369     /**
370      * Process Package.
371      *
372      * @param ast token
373      * @param nextToken next token
374      */
375     private void processPackage(DetailAST ast, DetailAST nextToken) {
376         if (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)) {
377             if (CheckUtil.isPackageInfo(getFilePath())) {
378                 if (!ast.getFirstChild().hasChildren() && !isPrecededByJavadoc(ast)) {
379                     log(ast, MSG_SHOULD_BE_SEPARATED, ast.getText());
380                 }
381             }
382             else {
383                 log(ast, MSG_SHOULD_BE_SEPARATED, ast.getText());
384             }
385         }
386         if (isLineEmptyAfterPackage(ast)) {
387             final DetailAST elementAst = getViolationAstForPackage(ast);
388             log(elementAst, MSG_SHOULD_BE_SEPARATED, elementAst.getText());
389         }
390         else if (!hasEmptyLineAfter(ast)) {
391             log(nextToken, MSG_SHOULD_BE_SEPARATED, nextToken.getText());
392         }
393     }
394 
395     /**
396      * Checks if there is another element at next line of package declaration.
397      *
398      * @param ast Package ast.
399      * @return true, if there is an element.
400      */
401     private static boolean isLineEmptyAfterPackage(DetailAST ast) {
402         DetailAST nextElement = ast;
403         final int lastChildLineNo = ast.getLastChild().getLineNo();
404         while (nextElement.getLineNo() < lastChildLineNo + 1
405                 && nextElement.getNextSibling() != null) {
406             nextElement = nextElement.getNextSibling();
407         }
408         return nextElement.getLineNo() == lastChildLineNo + 1;
409     }
410 
411     /**
412      * Gets the Ast on which violation is to be given for package declaration.
413      *
414      * @param ast Package ast.
415      * @return Violation ast.
416      */
417     private static DetailAST getViolationAstForPackage(DetailAST ast) {
418         DetailAST nextElement = ast;
419         final int lastChildLineNo = ast.getLastChild().getLineNo();
420         while (nextElement.getLineNo() < lastChildLineNo + 1) {
421             nextElement = nextElement.getNextSibling();
422         }
423         return nextElement;
424     }
425 
426     /**
427      * Process Import.
428      *
429      * @param ast token
430      * @param nextToken next token
431      */
432     private void processImport(DetailAST ast, DetailAST nextToken) {
433         if (!TokenUtil.isOfType(nextToken, TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT)
434             && !hasEmptyLineAfter(ast)) {
435             log(nextToken, MSG_SHOULD_BE_SEPARATED, nextToken.getText());
436         }
437     }
438 
439     /**
440      * Process Variable.
441      *
442      * @param ast token
443      * @param nextToken next Token
444      */
445     private void processVariableDef(DetailAST ast, DetailAST nextToken) {
446         if (isTypeField(ast) && !hasEmptyLineAfter(ast)
447                 && isViolatingEmptyLineBetweenFieldsPolicy(nextToken)) {
448             log(nextToken, MSG_SHOULD_BE_SEPARATED,
449                     nextToken.getText());
450         }
451     }
452 
453     /**
454      * Checks whether token placement violates policy of empty line between fields.
455      *
456      * @param detailAST token to be analyzed
457      * @return true if policy is violated and warning should be raised; false otherwise
458      */
459     private boolean isViolatingEmptyLineBetweenFieldsPolicy(DetailAST detailAST) {
460         return detailAST.getType() != TokenTypes.RCURLY
461                 && (!allowNoEmptyLineBetweenFields
462                     || !TokenUtil.isOfType(detailAST, TokenTypes.COMMA, TokenTypes.VARIABLE_DEF));
463     }
464 
465     /**
466      * Checks if a token has empty two previous lines and multiple empty lines is not allowed.
467      *
468      * @param token DetailAST token
469      * @return true, if token has empty two lines before and allowMultipleEmptyLines is false
470      */
471     private boolean hasNotAllowedTwoEmptyLinesBefore(DetailAST token) {
472         return !allowMultipleEmptyLines && hasEmptyLineBefore(token)
473                 && isPrePreviousLineEmpty(token);
474     }
475 
476     /**
477      * Check if group of comments located right before token has more than one previous empty line.
478      *
479      * @param token DetailAST token
480      */
481     private void checkComments(DetailAST token) {
482         if (!allowMultipleEmptyLines) {
483             if (TokenUtil.isOfType(token,
484                 TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT,
485                 TokenTypes.STATIC_IMPORT, TokenTypes.STATIC_INIT)) {
486                 DetailAST previousNode = token.getPreviousSibling();
487                 while (isCommentInBeginningOfLine(previousNode)) {
488                     if (hasEmptyLineBefore(previousNode) && isPrePreviousLineEmpty(previousNode)) {
489                         log(previousNode, MSG_MULTIPLE_LINES, previousNode.getText());
490                     }
491                     previousNode = previousNode.getPreviousSibling();
492                 }
493             }
494             else {
495                 checkCommentsInsideToken(token);
496             }
497         }
498     }
499 
500     /**
501      * Check if group of comments located at the start of token has more than one previous empty
502      * line.
503      *
504      * @param token DetailAST token
505      */
506     private void checkCommentsInsideToken(DetailAST token) {
507         final List<DetailAST> childNodes = new ArrayList<>();
508         DetailAST childNode = token.getLastChild();
509         while (childNode != null) {
510             if (childNode.getType() == TokenTypes.MODIFIERS) {
511                 for (DetailAST node = token.getFirstChild().getLastChild();
512                          node != null;
513                          node = node.getPreviousSibling()) {
514                     if (isCommentInBeginningOfLine(node)) {
515                         childNodes.add(node);
516                     }
517                 }
518             }
519             else if (isCommentInBeginningOfLine(childNode)) {
520                 childNodes.add(childNode);
521             }
522             childNode = childNode.getPreviousSibling();
523         }
524         for (DetailAST node : childNodes) {
525             if (hasEmptyLineBefore(node) && isPrePreviousLineEmpty(node)) {
526                 log(node, MSG_MULTIPLE_LINES, node.getText());
527             }
528         }
529     }
530 
531     /**
532      * Checks if a token has empty pre-previous line.
533      *
534      * @param token DetailAST token.
535      * @return true, if token has empty lines before.
536      */
537     private boolean isPrePreviousLineEmpty(DetailAST token) {
538         boolean result = false;
539         final int lineNo = token.getLineNo();
540         // 3 is the number of the pre-previous line because the numbering starts from zero.
541         final int number = 3;
542         if (lineNo >= number) {
543             final String prePreviousLine = getLine(lineNo - number);
544 
545             result = CommonUtil.isBlank(prePreviousLine);
546             final boolean previousLineIsEmpty = CommonUtil.isBlank(getLine(lineNo - 2));
547 
548             if (previousLineIsEmpty && result) {
549                 result = true;
550             }
551             else if (token.findFirstToken(TokenTypes.TYPE) != null) {
552                 result = isTwoPrecedingPreviousLinesFromCommentEmpty(token);
553             }
554         }
555         return result;
556 
557     }
558 
559     /**
560      * Checks if token has two preceding lines empty, starting from its describing comment.
561      *
562      * @param token token checked.
563      * @return true, if both previous and pre-previous lines from dependent comment are empty
564      */
565     private boolean isTwoPrecedingPreviousLinesFromCommentEmpty(DetailAST token) {
566         boolean upToPrePreviousLinesEmpty = false;
567 
568         for (DetailAST typeChild = token.findFirstToken(TokenTypes.TYPE).getLastChild();
569              typeChild != null; typeChild = typeChild.getPreviousSibling()) {
570 
571             if (isTokenNotOnPreviousSiblingLines(typeChild, token)) {
572 
573                 final String commentBeginningPreviousLine =
574                     getLine(typeChild.getLineNo() - 2);
575                 final String commentBeginningPrePreviousLine =
576                     getLine(typeChild.getLineNo() - 3);
577 
578                 if (CommonUtil.isBlank(commentBeginningPreviousLine)
579                     && CommonUtil.isBlank(commentBeginningPrePreviousLine)) {
580                     upToPrePreviousLinesEmpty = true;
581                     break;
582                 }
583 
584             }
585 
586         }
587 
588         return upToPrePreviousLinesEmpty;
589     }
590 
591     /**
592      * Checks if token is not placed on the realm of previous sibling of token's parent.
593      *
594      * @param token token checked.
595      * @param parentToken parent token.
596      * @return true, if child token doesn't occupy parent token's previous sibling's realm.
597      */
598     private static boolean isTokenNotOnPreviousSiblingLines(DetailAST token,
599                                                             DetailAST parentToken) {
600         DetailAST previousSibling = parentToken.getPreviousSibling();
601         for (DetailAST astNode = previousSibling; astNode != null;
602              astNode = astNode.getLastChild()) {
603             previousSibling = astNode;
604         }
605 
606         return token.getLineNo() != previousSibling.getLineNo();
607     }
608 
609     /**
610      * Checks if token have empty line after.
611      *
612      * @param token token.
613      * @return true if token have empty line after.
614      */
615     private boolean hasEmptyLineAfter(DetailAST token) {
616         DetailAST lastToken = token.getLastChild().getLastChild();
617         if (lastToken == null) {
618             lastToken = token.getLastChild();
619         }
620         DetailAST nextToken = token.getNextSibling();
621         if (TokenUtil.isCommentType(nextToken.getType())) {
622             nextToken = nextToken.getNextSibling();
623         }
624         // Start of the next token
625         final int nextBegin = nextToken.getLineNo();
626         // End of current token.
627         final int currentEnd = lastToken.getLineNo();
628         return hasEmptyLine(currentEnd + 1, nextBegin - 1);
629     }
630 
631     /**
632      * Finds comment in next sibling of given packageDef.
633      *
634      * @param packageDef token to check
635      * @return comment under the token
636      */
637     private static Optional<DetailAST> findCommentUnder(DetailAST packageDef) {
638         return Optional.ofNullable(packageDef.getNextSibling())
639             .map(sibling -> sibling.findFirstToken(TokenTypes.MODIFIERS))
640             .map(DetailAST::getFirstChild)
641             .filter(token -> TokenUtil.isCommentType(token.getType()))
642             .filter(comment -> comment.getLineNo() == packageDef.getLineNo() + 1);
643     }
644 
645     /**
646      * Checks, whether there are empty lines within the specified line range. Line numbering is
647      * started from 1 for parameter values
648      *
649      * @param startLine number of the first line in the range
650      * @param endLine number of the second line in the range
651      * @return {@code true} if found any blank line within the range, {@code false}
652      *         otherwise
653      */
654     private boolean hasEmptyLine(int startLine, int endLine) {
655         // Initial value is false - blank line not found
656         boolean result = false;
657         for (int line = startLine; line <= endLine; line++) {
658             // Check, if the line is blank. Lines are numbered from 0, so subtract 1
659             if (CommonUtil.isBlank(getLine(line - 1))) {
660                 result = true;
661                 break;
662             }
663         }
664         return result;
665     }
666 
667     /**
668      * Checks if a token has an empty line before.
669      *
670      * @param token token.
671      * @return true, if token have empty line before.
672      */
673     private boolean hasEmptyLineBefore(DetailAST token) {
674         boolean result = false;
675         final int lineNo = token.getLineNo();
676         if (lineNo != 1) {
677             // [lineNo - 2] is the number of the previous line as the numbering starts from zero.
678             final String lineBefore = getLine(lineNo - 2);
679 
680             if (CommonUtil.isBlank(lineBefore)) {
681                 result = true;
682             }
683             else if (token.findFirstToken(TokenTypes.TYPE) != null) {
684                 for (DetailAST typeChild = token.findFirstToken(TokenTypes.TYPE).getLastChild();
685                      typeChild != null && !result && typeChild.getLineNo() > 1;
686                      typeChild = typeChild.getPreviousSibling()) {
687 
688                     final String commentBeginningPreviousLine =
689                         getLine(typeChild.getLineNo() - 2);
690                     result = CommonUtil.isBlank(commentBeginningPreviousLine);
691 
692                 }
693             }
694         }
695         return result;
696     }
697 
698     /**
699      * Check if token is comment, which starting in beginning of line.
700      *
701      * @param comment comment token for check.
702      * @return true, if token is comment, which starting in beginning of line.
703      */
704     private boolean isCommentInBeginningOfLine(DetailAST comment) {
705         // comment.getLineNo() - 1 is the number of the previous line as the numbering starts
706         // from zero.
707         boolean result = false;
708         if (comment != null) {
709             final String lineWithComment = getLine(comment.getLineNo() - 1).trim();
710             result = lineWithComment.startsWith("//") || lineWithComment.startsWith("/*");
711         }
712         return result;
713     }
714 
715     /**
716      * Check if token is preceded by javadoc comment.
717      *
718      * @param token token for check.
719      * @return true, if token is preceded by javadoc comment.
720      */
721     private static boolean isPrecededByJavadoc(DetailAST token) {
722         boolean result = false;
723         final DetailAST previous = token.getPreviousSibling();
724         if (previous.getType() == TokenTypes.BLOCK_COMMENT_BEGIN
725                 && JavadocUtil.isJavadocComment(previous.getFirstChild().getText())) {
726             result = true;
727         }
728         return result;
729     }
730 
731     /**
732      * If variable definition is a type field.
733      *
734      * @param variableDef variable definition.
735      * @return true variable definition is a type field.
736      */
737     private static boolean isTypeField(DetailAST variableDef) {
738         return TokenUtil.isTypeDeclaration(variableDef.getParent().getParent().getType());
739     }
740 
741 }