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.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 after,
39   * fields, constructors, methods, nested classes,
40   * static initializers and instance initializers.
41   * </div>
42   *
43   * <p>
44   * For package declaration it checks for both before and after
45   * line separators and for import declarations it checks for
46   * empty line separator after last import declaration.
47   * </p>
48   *
49   * <p>
50   * Checks for empty line separators after not only statements but
51   * implementation and documentation comments and blocks as well.
52   * </p>
53   *
54   * <p>
55   * ATTENTION: empty line separator is required between token siblings,
56   * not after line where token is found.
57   * If token does not have a sibling of the same type, then empty line
58   * is required at its end (for example for CLASS_DEF it is after '}').
59   * Also, trailing comments are skipped.
60   * </p>
61   *
62   * @since 5.8
63   */
64  @StatelessCheck
65  public class EmptyLineSeparatorCheck extends AbstractCheck {
66  
67      /**
68       * A key is pointing to the warning message empty.line.separator in "messages.properties"
69       * file.
70       */
71      public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";
72  
73      /**
74       * A key is pointing to the warning message empty.line.separator.multiple.lines
75       *  in "messages.properties"
76       * file.
77       */
78      public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";
79  
80      /**
81       * A key is pointing to the warning message empty.line.separator.lines.after
82       * in "messages.properties" file.
83       */
84      public static final String MSG_MULTIPLE_LINES_AFTER =
85              "empty.line.separator.multiple.lines.after";
86  
87      /**
88       * A key is pointing to the warning message empty.line.separator.multiple.lines.inside
89       * in "messages.properties" file.
90       */
91      public static final String MSG_MULTIPLE_LINES_INSIDE =
92              "empty.line.separator.multiple.lines.inside";
93  
94      /** Allow no empty line between fields. */
95      private boolean allowNoEmptyLineBetweenFields;
96  
97      /** Allow multiple empty lines between class members. */
98      private boolean allowMultipleEmptyLines = true;
99  
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 }