001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2019 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.whitespace;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.LinkedList;
025import java.util.List;
026
027import com.puppycrawl.tools.checkstyle.StatelessCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
029import com.puppycrawl.tools.checkstyle.api.DetailAST;
030import com.puppycrawl.tools.checkstyle.api.FileContents;
031import com.puppycrawl.tools.checkstyle.api.TokenTypes;
032import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
033import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
034
035/**
036 * <p>
037 * Checks for empty line separators after header, package, all import declarations,
038 * fields, constructors, methods, nested classes,
039 * static initializers and instance initializers.
040 * </p>
041 * <p>
042 * ATTENTION: empty line separator is required between AST siblings,
043 * not after line where token is found.
044 * </p>
045 * <ul>
046 * <li>
047 * Property {@code allowNoEmptyLineBetweenFields} - Allow no empty line between fields.
048 * Default value is {@code false}.
049 * </li>
050 * <li>
051 * Property {@code allowMultipleEmptyLines} - Allow multiple empty lines between class members.
052 * Default value is {@code true}.
053 * </li>
054 * <li>
055 * Property {@code allowMultipleEmptyLinesInsideClassMembers} - Allow multiple
056 * empty lines inside class members.
057 * Default value is {@code true}.
058 * </li>
059 * <li>
060 * Property {@code tokens} - tokens to check
061 * Default value is:
062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PACKAGE_DEF">
063 * PACKAGE_DEF</a>,
064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IMPORT">
065 * IMPORT</a>,
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_IMPORT">
067 * STATIC_IMPORT</a>,
068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
069 * CLASS_DEF</a>,
070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
071 * INTERFACE_DEF</a>,
072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
073 * ENUM_DEF</a>,
074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
075 * STATIC_INIT</a>,
076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
077 * INSTANCE_INIT</a>,
078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
079 * METHOD_DEF</a>,
080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
081 * CTOR_DEF</a>,
082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
083 * VARIABLE_DEF</a>.
084 * </li>
085 * </ul>
086 * <p>
087 * Example of declarations without empty line separator:
088 * </p>
089 *
090 * <pre>
091 * ///////////////////////////////////////////////////
092 * //HEADER
093 * ///////////////////////////////////////////////////
094 * package com.puppycrawl.tools.checkstyle.whitespace;
095 * import java.io.Serializable;
096 * class Foo
097 * {
098 *   public static final int FOO_CONST = 1;
099 *   public void foo() {} //should be separated from previous statement.
100 * }
101 * </pre>
102 *
103 * <p>
104 * To configure the check with default parameters:
105 * </p>
106 *
107 * <pre>
108 * &lt;module name=&quot;EmptyLineSeparator&quot;/&gt;
109 * </pre>
110 *
111 * <p>
112 * Example of declarations with empty line separator
113 * that is expected by the Check by default:
114 * </p>
115 *
116 * <pre>
117 * ///////////////////////////////////////////////////
118 * //HEADER
119 * ///////////////////////////////////////////////////
120 *
121 * package com.puppycrawl.tools.checkstyle.whitespace;
122 *
123 * import java.io.Serializable;
124 *
125 * class Foo
126 * {
127 *   public static final int FOO_CONST = 1;
128 *
129 *   public void foo() {}
130 * }
131 * </pre>
132 * <p>
133 * To check empty line after
134 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
135 * VARIABLE_DEF</a> and
136 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
137 * METHOD_DEF</a>:
138 * </p>
139 *
140 * <pre>
141 * &lt;module name=&quot;EmptyLineSeparator&quot;&gt;
142 *   &lt;property name=&quot;tokens&quot; value=&quot;VARIABLE_DEF, METHOD_DEF&quot;/&gt;
143 * &lt;/module&gt;
144 * </pre>
145 *
146 * <p>
147 * To allow no empty line between fields:
148 * </p>
149 * <pre>
150 * &lt;module name="EmptyLineSeparator"&gt;
151 *   &lt;property name="allowNoEmptyLineBetweenFields" value="true"/&gt;
152 * &lt;/module&gt;
153 * </pre>
154 *
155 * <p>
156 * Example of declarations with multiple empty lines between class members (allowed by default):
157 * </p>
158 *
159 * <pre>
160 * ///////////////////////////////////////////////////
161 * //HEADER
162 * ///////////////////////////////////////////////////
163 *
164 *
165 * package com.puppycrawl.tools.checkstyle.whitespace;
166 *
167 *
168 *
169 * import java.io.Serializable;
170 *
171 *
172 * class Foo
173 * {
174 *   public static final int FOO_CONST = 1;
175 *
176 *
177 *
178 *   public void foo() {} //should be separated from previous statement.
179 * }
180 * </pre>
181 * <p>
182 * To disallow multiple empty lines between class members:
183 * </p>
184 * <pre>
185 * &lt;module name=&quot;EmptyLineSeparator&quot;&gt;
186 *   &lt;property name=&quot;allowMultipleEmptyLines&quot; value=&quot;false&quot;/&gt;
187 * &lt;/module&gt;
188 * </pre>
189 *
190 * <p>
191 * To disallow multiple empty lines inside constructor, initialization block and method:
192 * </p>
193 * <pre>
194 * &lt;module name="EmptyLineSeparator"&gt;
195 *   &lt;property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/&gt;
196 * &lt;/module&gt;
197 * </pre>
198 *
199 * <p>
200 * The check is valid only for statements that have body:
201 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
202 * CLASS_DEF</a>,
203 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
204 * INTERFACE_DEF</a>,
205 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
206 * ENUM_DEF</a>,
207 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
208 * STATIC_INIT</a>,
209 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
210 * INSTANCE_INIT</a>,
211 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
212 * METHOD_DEF</a>,
213 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
214 * CTOR_DEF</a>.
215 * </p>
216 * <p>
217 * Example of declarations with multiple empty lines inside method:
218 * </p>
219 *
220 * <pre>
221 * ///////////////////////////////////////////////////
222 * //HEADER
223 * ///////////////////////////////////////////////////
224 *
225 * package com.puppycrawl.tools.checkstyle.whitespace;
226 *
227 * class Foo
228 * {
229 *
230 *   public void foo() {
231 *
232 *
233 *     System.out.println(1); // violation since method has 2 empty lines subsequently
234 *   }
235 * }
236 * </pre>
237 *
238 * @since 5.8
239 */
240@StatelessCheck
241public class EmptyLineSeparatorCheck extends AbstractCheck {
242
243    /**
244     * A key is pointing to the warning message empty.line.separator in "messages.properties"
245     * file.
246     */
247    public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";
248
249    /**
250     * A key is pointing to the warning message empty.line.separator.multiple.lines
251     *  in "messages.properties"
252     * file.
253     */
254    public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";
255
256    /**
257     * A key is pointing to the warning message empty.line.separator.lines.after
258     * in "messages.properties" file.
259     */
260    public static final String MSG_MULTIPLE_LINES_AFTER =
261            "empty.line.separator.multiple.lines.after";
262
263    /**
264     * A key is pointing to the warning message empty.line.separator.multiple.lines.inside
265     * in "messages.properties" file.
266     */
267    public static final String MSG_MULTIPLE_LINES_INSIDE =
268            "empty.line.separator.multiple.lines.inside";
269
270    /** List of AST token types, which can not have comment nodes to check inside. */
271    private static final List<Integer> TOKEN_TYPES_WITHOUT_COMMENTS_TO_CHECK_INSIDE =
272            Arrays.asList(TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT,
273                    TokenTypes.STATIC_INIT);
274
275    /** Allow no empty line between fields. */
276    private boolean allowNoEmptyLineBetweenFields;
277
278    /** Allow multiple empty lines between class members. */
279    private boolean allowMultipleEmptyLines = true;
280
281    /** Allow multiple empty lines inside class members. */
282    private boolean allowMultipleEmptyLinesInsideClassMembers = true;
283
284    /**
285     * Setter to allow no empty line between fields.
286     * @param allow
287     *        User's value.
288     */
289    public final void setAllowNoEmptyLineBetweenFields(boolean allow) {
290        allowNoEmptyLineBetweenFields = allow;
291    }
292
293    /**
294     * Setter to allow multiple empty lines between class members.
295     * @param allow User's value.
296     */
297    public void setAllowMultipleEmptyLines(boolean allow) {
298        allowMultipleEmptyLines = allow;
299    }
300
301    /**
302     * Setter to allow multiple empty lines inside class members.
303     * @param allow User's value.
304     */
305    public void setAllowMultipleEmptyLinesInsideClassMembers(boolean allow) {
306        allowMultipleEmptyLinesInsideClassMembers = allow;
307    }
308
309    @Override
310    public boolean isCommentNodesRequired() {
311        return true;
312    }
313
314    @Override
315    public int[] getDefaultTokens() {
316        return getAcceptableTokens();
317    }
318
319    @Override
320    public int[] getAcceptableTokens() {
321        return new int[] {
322            TokenTypes.PACKAGE_DEF,
323            TokenTypes.IMPORT,
324            TokenTypes.STATIC_IMPORT,
325            TokenTypes.CLASS_DEF,
326            TokenTypes.INTERFACE_DEF,
327            TokenTypes.ENUM_DEF,
328            TokenTypes.STATIC_INIT,
329            TokenTypes.INSTANCE_INIT,
330            TokenTypes.METHOD_DEF,
331            TokenTypes.CTOR_DEF,
332            TokenTypes.VARIABLE_DEF,
333        };
334    }
335
336    @Override
337    public int[] getRequiredTokens() {
338        return CommonUtil.EMPTY_INT_ARRAY;
339    }
340
341    @Override
342    public void visitToken(DetailAST ast) {
343        checkComments(ast);
344        if (hasMultipleLinesBefore(ast)) {
345            log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText());
346        }
347        if (!allowMultipleEmptyLinesInsideClassMembers) {
348            processMultipleLinesInside(ast);
349        }
350
351        DetailAST nextToken = ast.getNextSibling();
352        while (nextToken != null && isComment(nextToken)) {
353            nextToken = nextToken.getNextSibling();
354        }
355        if (nextToken != null) {
356            final int astType = ast.getType();
357            switch (astType) {
358                case TokenTypes.VARIABLE_DEF:
359                    processVariableDef(ast, nextToken);
360                    break;
361                case TokenTypes.IMPORT:
362                case TokenTypes.STATIC_IMPORT:
363                    processImport(ast, nextToken);
364                    break;
365                case TokenTypes.PACKAGE_DEF:
366                    processPackage(ast, nextToken);
367                    break;
368                default:
369                    if (nextToken.getType() == TokenTypes.RCURLY) {
370                        if (hasNotAllowedTwoEmptyLinesBefore(nextToken)) {
371                            log(ast.getLineNo(), MSG_MULTIPLE_LINES_AFTER, ast.getText());
372                        }
373                    }
374                    else if (!hasEmptyLineAfter(ast)) {
375                        log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED,
376                            nextToken.getText());
377                    }
378            }
379        }
380    }
381
382    /**
383     * Log violation in case there are multiple empty lines inside constructor,
384     * initialization block or method.
385     * @param ast the ast to check.
386     */
387    private void processMultipleLinesInside(DetailAST ast) {
388        final int astType = ast.getType();
389        if (isClassMemberBlock(astType)) {
390            final List<Integer> emptyLines = getEmptyLines(ast);
391            final List<Integer> emptyLinesToLog = getEmptyLinesToLog(emptyLines);
392
393            for (Integer lineNo : emptyLinesToLog) {
394                // Checkstyle counts line numbers from 0 but IDE from 1
395                log(lineNo + 1, MSG_MULTIPLE_LINES_INSIDE);
396            }
397        }
398    }
399
400    /**
401     * Whether the AST is a class member block.
402     * @param astType the AST to check.
403     * @return true if the AST is a class member block.
404     */
405    private static boolean isClassMemberBlock(int astType) {
406        return astType == TokenTypes.STATIC_INIT
407                || astType == TokenTypes.INSTANCE_INIT
408                || astType == TokenTypes.METHOD_DEF
409                || astType == TokenTypes.CTOR_DEF;
410    }
411
412    /**
413     * Get list of empty lines.
414     * @param ast the ast to check.
415     * @return list of line numbers for empty lines.
416     */
417    private List<Integer> getEmptyLines(DetailAST ast) {
418        final DetailAST lastToken = ast.getLastChild().getLastChild();
419        int lastTokenLineNo = 0;
420        if (lastToken != null) {
421            // -1 as count starts from 0
422            // -2 as last token line cannot be empty, because it is a RCURLY
423            lastTokenLineNo = lastToken.getLineNo() - 2;
424        }
425        final List<Integer> emptyLines = new ArrayList<>();
426        final FileContents fileContents = getFileContents();
427
428        for (int lineNo = ast.getLineNo(); lineNo <= lastTokenLineNo; lineNo++) {
429            if (fileContents.lineIsBlank(lineNo)) {
430                emptyLines.add(lineNo);
431            }
432        }
433        return emptyLines;
434    }
435
436    /**
437     * Get list of empty lines to log.
438     * @param emptyLines list of empty lines.
439     * @return list of empty lines to log.
440     */
441    private static List<Integer> getEmptyLinesToLog(List<Integer> emptyLines) {
442        final List<Integer> emptyLinesToLog = new ArrayList<>();
443        if (emptyLines.size() >= 2) {
444            int previousEmptyLineNo = emptyLines.get(0);
445            for (int emptyLineNo : emptyLines) {
446                if (previousEmptyLineNo + 1 == emptyLineNo) {
447                    emptyLinesToLog.add(emptyLineNo);
448                }
449                previousEmptyLineNo = emptyLineNo;
450            }
451        }
452        return emptyLinesToLog;
453    }
454
455    /**
456     * Whether the token has not allowed multiple empty lines before.
457     * @param ast the ast to check.
458     * @return true if the token has not allowed multiple empty lines before.
459     */
460    private boolean hasMultipleLinesBefore(DetailAST ast) {
461        boolean result = false;
462        if ((ast.getType() != TokenTypes.VARIABLE_DEF
463            || isTypeField(ast))
464                && hasNotAllowedTwoEmptyLinesBefore(ast)) {
465            result = true;
466        }
467        return result;
468    }
469
470    /**
471     * Process Package.
472     * @param ast token
473     * @param nextToken next token
474     */
475    private void processPackage(DetailAST ast, DetailAST nextToken) {
476        if (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)) {
477            if (getFileContents().getFileName().endsWith("package-info.java")) {
478                if (ast.getFirstChild().getChildCount() == 0 && !isPrecededByJavadoc(ast)) {
479                    log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText());
480                }
481            }
482            else {
483                log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText());
484            }
485        }
486        if (!hasEmptyLineAfter(ast)) {
487            log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText());
488        }
489    }
490
491    /**
492     * Process Import.
493     * @param ast token
494     * @param nextToken next token
495     */
496    private void processImport(DetailAST ast, DetailAST nextToken) {
497        if (nextToken.getType() != TokenTypes.IMPORT
498                && nextToken.getType() != TokenTypes.STATIC_IMPORT && !hasEmptyLineAfter(ast)) {
499            log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText());
500        }
501    }
502
503    /**
504     * Process Variable.
505     * @param ast token
506     * @param nextToken next Token
507     */
508    private void processVariableDef(DetailAST ast, DetailAST nextToken) {
509        if (isTypeField(ast) && !hasEmptyLineAfter(ast)
510                && isViolatingEmptyLineBetweenFieldsPolicy(nextToken)) {
511            log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED,
512                    nextToken.getText());
513        }
514    }
515
516    /**
517     * Checks whether token placement violates policy of empty line between fields.
518     * @param detailAST token to be analyzed
519     * @return true if policy is violated and warning should be raised; false otherwise
520     */
521    private boolean isViolatingEmptyLineBetweenFieldsPolicy(DetailAST detailAST) {
522        return detailAST.getType() != TokenTypes.RCURLY
523                && (!allowNoEmptyLineBetweenFields
524                    || detailAST.getType() != TokenTypes.VARIABLE_DEF);
525    }
526
527    /**
528     * Checks if a token has empty two previous lines and multiple empty lines is not allowed.
529     * @param token DetailAST token
530     * @return true, if token has empty two lines before and allowMultipleEmptyLines is false
531     */
532    private boolean hasNotAllowedTwoEmptyLinesBefore(DetailAST token) {
533        return !allowMultipleEmptyLines && hasEmptyLineBefore(token)
534                && isPrePreviousLineEmpty(token);
535    }
536
537    /**
538     * Check if group of comments located right before token has more than one previous empty line.
539     * @param token DetailAST token
540     */
541    private void checkComments(DetailAST token) {
542        if (!allowMultipleEmptyLines) {
543            if (TOKEN_TYPES_WITHOUT_COMMENTS_TO_CHECK_INSIDE.contains(token.getType())) {
544                DetailAST previousNode = token.getPreviousSibling();
545                while (isCommentInBeginningOfLine(previousNode)) {
546                    if (hasEmptyLineBefore(previousNode) && isPrePreviousLineEmpty(previousNode)) {
547                        log(previousNode, MSG_MULTIPLE_LINES, previousNode.getText());
548                    }
549                    previousNode = previousNode.getPreviousSibling();
550                }
551            }
552            else {
553                checkCommentsInsideToken(token);
554            }
555        }
556    }
557
558    /**
559     * Check if group of comments located at the start of token has more than one previous empty
560     * line.
561     * @param token DetailAST token
562     */
563    private void checkCommentsInsideToken(DetailAST token) {
564        final List<DetailAST> childNodes = new LinkedList<>();
565        DetailAST childNode = token.getLastChild();
566        while (childNode != null) {
567            if (childNode.getType() == TokenTypes.MODIFIERS) {
568                for (DetailAST node = token.getFirstChild().getLastChild();
569                         node != null;
570                         node = node.getPreviousSibling()) {
571                    if (isCommentInBeginningOfLine(node)) {
572                        childNodes.add(node);
573                    }
574                }
575            }
576            else if (isCommentInBeginningOfLine(childNode)) {
577                childNodes.add(childNode);
578            }
579            childNode = childNode.getPreviousSibling();
580        }
581        for (DetailAST node : childNodes) {
582            if (hasEmptyLineBefore(node) && isPrePreviousLineEmpty(node)) {
583                log(node, MSG_MULTIPLE_LINES, node.getText());
584            }
585        }
586    }
587
588    /**
589     * Checks if a token has empty pre-previous line.
590     * @param token DetailAST token.
591     * @return true, if token has empty lines before.
592     */
593    private boolean isPrePreviousLineEmpty(DetailAST token) {
594        boolean result = false;
595        final int lineNo = token.getLineNo();
596        // 3 is the number of the pre-previous line because the numbering starts from zero.
597        final int number = 3;
598        if (lineNo >= number) {
599            final String prePreviousLine = getLines()[lineNo - number];
600            result = CommonUtil.isBlank(prePreviousLine);
601        }
602        return result;
603    }
604
605    /**
606     * Checks if token have empty line after.
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 (isComment(nextToken)) {
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     * Checks, whether there are empty lines within the specified line range. Line numbering is
628     * started from 1 for parameter values
629     * @param startLine number of the first line in the range
630     * @param endLine number of the second line in the range
631     * @return {@code true} if found any blank line within the range, {@code false}
632     *         otherwise
633     */
634    private boolean hasEmptyLine(int startLine, int endLine) {
635        // Initial value is false - blank line not found
636        boolean result = false;
637        final FileContents fileContents = getFileContents();
638        for (int line = startLine; line <= endLine; line++) {
639            // Check, if the line is blank. Lines are numbered from 0, so subtract 1
640            if (fileContents.lineIsBlank(line - 1)) {
641                result = true;
642                break;
643            }
644        }
645        return result;
646    }
647
648    /**
649     * Checks if a token has a empty line before.
650     * @param token token.
651     * @return true, if token have empty line before.
652     */
653    private boolean hasEmptyLineBefore(DetailAST token) {
654        boolean result = false;
655        final int lineNo = token.getLineNo();
656        if (lineNo != 1) {
657            // [lineNo - 2] is the number of the previous line as the numbering starts from zero.
658            final String lineBefore = getLines()[lineNo - 2];
659            result = CommonUtil.isBlank(lineBefore);
660        }
661        return result;
662    }
663
664    /**
665     * Check if token is comment, which starting in beginning of line.
666     * @param comment comment token for check.
667     * @return true, if token is comment, which starting in beginning of line.
668     */
669    private boolean isCommentInBeginningOfLine(DetailAST comment) {
670        // [comment.getLineNo() - 1] is the number of the previous line as the numbering starts
671        // from zero.
672        boolean result = false;
673        if (comment != null) {
674            final String lineWithComment = getLines()[comment.getLineNo() - 1].trim();
675            result = lineWithComment.startsWith("//") || lineWithComment.startsWith("/*");
676        }
677        return result;
678    }
679
680    /**
681     * Check if token is preceded by javadoc comment.
682     * @param token token for check.
683     * @return true, if token is preceded by javadoc comment.
684     */
685    private static boolean isPrecededByJavadoc(DetailAST token) {
686        boolean result = false;
687        final DetailAST previous = token.getPreviousSibling();
688        if (previous.getType() == TokenTypes.BLOCK_COMMENT_BEGIN
689                && JavadocUtil.isJavadocComment(previous.getFirstChild().getText())) {
690            result = true;
691        }
692        return result;
693    }
694
695    /**
696     * Check if token is a comment.
697     * @param ast ast node
698     * @return true, if given ast is comment.
699     */
700    private static boolean isComment(DetailAST ast) {
701        return ast.getType() == TokenTypes.SINGLE_LINE_COMMENT
702                   || ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN;
703    }
704
705    /**
706     * If variable definition is a type field.
707     * @param variableDef variable definition.
708     * @return true variable definition is a type field.
709     */
710    private static boolean isTypeField(DetailAST variableDef) {
711        final int parentType = variableDef.getParent().getParent().getType();
712        return parentType == TokenTypes.CLASS_DEF;
713    }
714
715}