001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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; 026import java.util.Optional; 027 028import com.puppycrawl.tools.checkstyle.StatelessCheck; 029import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 030import com.puppycrawl.tools.checkstyle.api.DetailAST; 031import com.puppycrawl.tools.checkstyle.api.FileContents; 032import com.puppycrawl.tools.checkstyle.api.TokenTypes; 033import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 034import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 035 036/** 037 * <p> 038 * Checks for empty line separators after header, package, all import declarations, 039 * fields, constructors, methods, nested classes, 040 * static initializers and instance initializers. 041 * </p> 042 * <p> 043 * ATTENTION: empty line separator is required between token siblings, 044 * not after line where token is found. 045 * If token does not have same type sibling then empty line 046 * is required at its end (for example for CLASS_DEF it is after '}'). 047 * Also, trailing comments are skipped. 048 * </p> 049 * <ul> 050 * <li> 051 * Property {@code allowNoEmptyLineBetweenFields} - Allow no empty line between fields. 052 * Default value is {@code false}. 053 * </li> 054 * <li> 055 * Property {@code allowMultipleEmptyLines} - Allow multiple empty lines between class members. 056 * Default value is {@code true}. 057 * </li> 058 * <li> 059 * Property {@code allowMultipleEmptyLinesInsideClassMembers} - Allow multiple 060 * empty lines inside class members. 061 * Default value is {@code true}. 062 * </li> 063 * <li> 064 * Property {@code tokens} - tokens to check 065 * Default value is: 066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PACKAGE_DEF"> 067 * PACKAGE_DEF</a>, 068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#IMPORT"> 069 * IMPORT</a>, 070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_IMPORT"> 071 * STATIC_IMPORT</a>, 072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 073 * CLASS_DEF</a>, 074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 075 * INTERFACE_DEF</a>, 076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 077 * ENUM_DEF</a>, 078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 079 * STATIC_INIT</a>, 080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT"> 081 * INSTANCE_INIT</a>, 082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 083 * METHOD_DEF</a>, 084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 085 * CTOR_DEF</a>, 086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 087 * VARIABLE_DEF</a>. 088 * </li> 089 * </ul> 090 * <p> 091 * Example of declarations without empty line separator: 092 * </p> 093 * 094 * <pre> 095 * /////////////////////////////////////////////////// 096 * //HEADER 097 * /////////////////////////////////////////////////// 098 * package com.puppycrawl.tools.checkstyle.whitespace; 099 * import java.io.Serializable; 100 * class Foo { 101 * public static final int FOO_CONST = 1; 102 * public void foo() {} //should be separated from previous statement. 103 * } 104 * </pre> 105 * 106 * <p> 107 * To configure the check with default parameters: 108 * </p> 109 * 110 * <pre> 111 * <module name="EmptyLineSeparator"/> 112 * </pre> 113 * 114 * <p> 115 * Example of declarations with empty line separator 116 * that is expected by the Check by default: 117 * </p> 118 * 119 * <pre> 120 * /////////////////////////////////////////////////// 121 * //HEADER 122 * /////////////////////////////////////////////////// 123 * 124 * package com.puppycrawl.tools.checkstyle.whitespace; 125 * 126 * import java.io.Serializable; 127 * 128 * class Foo { 129 * public static final int FOO_CONST = 1; 130 * 131 * public void foo() {} 132 * } 133 * </pre> 134 * <p> 135 * To check empty line after 136 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 137 * VARIABLE_DEF</a> and 138 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 139 * METHOD_DEF</a>: 140 * </p> 141 * 142 * <pre> 143 * <module name="EmptyLineSeparator"> 144 * <property name="tokens" value="VARIABLE_DEF, METHOD_DEF"/> 145 * </module> 146 * </pre> 147 * 148 * <p> 149 * To allow no empty line between fields: 150 * </p> 151 * <pre> 152 * <module name="EmptyLineSeparator"> 153 * <property name="allowNoEmptyLineBetweenFields" value="true"/> 154 * </module> 155 * </pre> 156 * 157 * <p> 158 * Example of declarations with multiple empty lines between class members (allowed by default): 159 * </p> 160 * 161 * <pre> 162 * /////////////////////////////////////////////////// 163 * //HEADER 164 * /////////////////////////////////////////////////// 165 * 166 * 167 * package com.puppycrawl.tools.checkstyle.whitespace; 168 * 169 * 170 * 171 * import java.io.Serializable; 172 * 173 * 174 * class Foo { 175 * public static final int FOO_CONST = 1; 176 * 177 * 178 * 179 * public void foo() {} //should be separated from previous statement. 180 * } 181 * </pre> 182 * <p> 183 * To disallow multiple empty lines between class members: 184 * </p> 185 * <pre> 186 * <module name="EmptyLineSeparator"> 187 * <property name="allowMultipleEmptyLines" value="false"/> 188 * </module> 189 * </pre> 190 * 191 * <p> 192 * To disallow multiple empty lines inside constructor, initialization block and method: 193 * </p> 194 * <pre> 195 * <module name="EmptyLineSeparator"> 196 * <property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/> 197 * </module> 198 * </pre> 199 * 200 * <p> 201 * The check is valid only for statements that have body: 202 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 203 * CLASS_DEF</a>, 204 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 205 * INTERFACE_DEF</a>, 206 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 207 * ENUM_DEF</a>, 208 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 209 * STATIC_INIT</a>, 210 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT"> 211 * INSTANCE_INIT</a>, 212 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 213 * METHOD_DEF</a>, 214 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 215 * CTOR_DEF</a>. 216 * </p> 217 * <p> 218 * Example of declarations with multiple empty lines inside method: 219 * </p> 220 * 221 * <pre> 222 * /////////////////////////////////////////////////// 223 * //HEADER 224 * /////////////////////////////////////////////////// 225 * 226 * package com.puppycrawl.tools.checkstyle.whitespace; 227 * 228 * class Foo { 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 * 287 * @param allow 288 * User's value. 289 */ 290 public final void setAllowNoEmptyLineBetweenFields(boolean allow) { 291 allowNoEmptyLineBetweenFields = allow; 292 } 293 294 /** 295 * Setter to allow multiple empty lines between class members. 296 * 297 * @param allow User's value. 298 */ 299 public void setAllowMultipleEmptyLines(boolean allow) { 300 allowMultipleEmptyLines = allow; 301 } 302 303 /** 304 * Setter to allow multiple empty lines inside class members. 305 * 306 * @param allow User's value. 307 */ 308 public void setAllowMultipleEmptyLinesInsideClassMembers(boolean allow) { 309 allowMultipleEmptyLinesInsideClassMembers = allow; 310 } 311 312 @Override 313 public boolean isCommentNodesRequired() { 314 return true; 315 } 316 317 @Override 318 public int[] getDefaultTokens() { 319 return getAcceptableTokens(); 320 } 321 322 @Override 323 public int[] getAcceptableTokens() { 324 return new int[] { 325 TokenTypes.PACKAGE_DEF, 326 TokenTypes.IMPORT, 327 TokenTypes.STATIC_IMPORT, 328 TokenTypes.CLASS_DEF, 329 TokenTypes.INTERFACE_DEF, 330 TokenTypes.ENUM_DEF, 331 TokenTypes.STATIC_INIT, 332 TokenTypes.INSTANCE_INIT, 333 TokenTypes.METHOD_DEF, 334 TokenTypes.CTOR_DEF, 335 TokenTypes.VARIABLE_DEF, 336 }; 337 } 338 339 @Override 340 public int[] getRequiredTokens() { 341 return CommonUtil.EMPTY_INT_ARRAY; 342 } 343 344 @Override 345 public void visitToken(DetailAST ast) { 346 checkComments(ast); 347 if (hasMultipleLinesBefore(ast)) { 348 log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText()); 349 } 350 if (!allowMultipleEmptyLinesInsideClassMembers) { 351 processMultipleLinesInside(ast); 352 } 353 if (ast.getType() == TokenTypes.PACKAGE_DEF) { 354 checkCommentInModifiers(ast); 355 } 356 DetailAST nextToken = ast.getNextSibling(); 357 while (nextToken != null && isComment(nextToken)) { 358 nextToken = nextToken.getNextSibling(); 359 } 360 if (nextToken != null) { 361 checkToken(ast, nextToken); 362 } 363 } 364 365 /** 366 * Checks that token and next token are separated. 367 * 368 * @param ast token to validate 369 * @param nextToken next sibling of the token 370 */ 371 private void checkToken(DetailAST ast, DetailAST nextToken) { 372 final int astType = ast.getType(); 373 switch (astType) { 374 case TokenTypes.VARIABLE_DEF: 375 processVariableDef(ast, nextToken); 376 break; 377 case TokenTypes.IMPORT: 378 case TokenTypes.STATIC_IMPORT: 379 processImport(ast, nextToken); 380 break; 381 case TokenTypes.PACKAGE_DEF: 382 processPackage(ast, nextToken); 383 break; 384 default: 385 if (nextToken.getType() == TokenTypes.RCURLY) { 386 if (hasNotAllowedTwoEmptyLinesBefore(nextToken)) { 387 log(ast.getLineNo(), MSG_MULTIPLE_LINES_AFTER, ast.getText()); 388 } 389 } 390 else if (!hasEmptyLineAfter(ast)) { 391 log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, 392 nextToken.getText()); 393 } 394 } 395 } 396 397 /** 398 * Checks that packageDef token is separated from comment in modifiers. 399 * 400 * @param packageDef package def token 401 */ 402 private void checkCommentInModifiers(DetailAST packageDef) { 403 final Optional<DetailAST> comment = findCommentUnder(packageDef); 404 if (comment.isPresent()) { 405 log(comment.get().getLineNo(), MSG_SHOULD_BE_SEPARATED, comment.get().getText()); 406 } 407 } 408 409 /** 410 * Log violation in case there are multiple empty lines inside constructor, 411 * initialization block or method. 412 * 413 * @param ast the ast to check. 414 */ 415 private void processMultipleLinesInside(DetailAST ast) { 416 final int astType = ast.getType(); 417 if (isClassMemberBlock(astType)) { 418 final List<Integer> emptyLines = getEmptyLines(ast); 419 final List<Integer> emptyLinesToLog = getEmptyLinesToLog(emptyLines); 420 421 for (Integer lineNo : emptyLinesToLog) { 422 // Checkstyle counts line numbers from 0 but IDE from 1 423 log(lineNo + 1, MSG_MULTIPLE_LINES_INSIDE); 424 } 425 } 426 } 427 428 /** 429 * Whether the AST is a class member block. 430 * 431 * @param astType the AST to check. 432 * @return true if the AST is a class member block. 433 */ 434 private static boolean isClassMemberBlock(int astType) { 435 return astType == TokenTypes.STATIC_INIT 436 || astType == TokenTypes.INSTANCE_INIT 437 || astType == TokenTypes.METHOD_DEF 438 || astType == TokenTypes.CTOR_DEF; 439 } 440 441 /** 442 * Get list of empty lines. 443 * 444 * @param ast the ast to check. 445 * @return list of line numbers for empty lines. 446 */ 447 private List<Integer> getEmptyLines(DetailAST ast) { 448 final DetailAST lastToken = ast.getLastChild().getLastChild(); 449 int lastTokenLineNo = 0; 450 if (lastToken != null) { 451 // -1 as count starts from 0 452 // -2 as last token line cannot be empty, because it is a RCURLY 453 lastTokenLineNo = lastToken.getLineNo() - 2; 454 } 455 final List<Integer> emptyLines = new ArrayList<>(); 456 final FileContents fileContents = getFileContents(); 457 458 for (int lineNo = ast.getLineNo(); lineNo <= lastTokenLineNo; lineNo++) { 459 if (fileContents.lineIsBlank(lineNo)) { 460 emptyLines.add(lineNo); 461 } 462 } 463 return emptyLines; 464 } 465 466 /** 467 * Get list of empty lines to log. 468 * 469 * @param emptyLines list of empty lines. 470 * @return list of empty lines to log. 471 */ 472 private static List<Integer> getEmptyLinesToLog(List<Integer> emptyLines) { 473 final List<Integer> emptyLinesToLog = new ArrayList<>(); 474 if (emptyLines.size() >= 2) { 475 int previousEmptyLineNo = emptyLines.get(0); 476 for (int emptyLineNo : emptyLines) { 477 if (previousEmptyLineNo + 1 == emptyLineNo) { 478 emptyLinesToLog.add(emptyLineNo); 479 } 480 previousEmptyLineNo = emptyLineNo; 481 } 482 } 483 return emptyLinesToLog; 484 } 485 486 /** 487 * Whether the token has not allowed multiple empty lines before. 488 * 489 * @param ast the ast to check. 490 * @return true if the token has not allowed multiple empty lines before. 491 */ 492 private boolean hasMultipleLinesBefore(DetailAST ast) { 493 boolean result = false; 494 if ((ast.getType() != TokenTypes.VARIABLE_DEF 495 || isTypeField(ast)) 496 && hasNotAllowedTwoEmptyLinesBefore(ast)) { 497 result = true; 498 } 499 return result; 500 } 501 502 /** 503 * Process Package. 504 * 505 * @param ast token 506 * @param nextToken next token 507 */ 508 private void processPackage(DetailAST ast, DetailAST nextToken) { 509 if (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)) { 510 if (getFileContents().getFileName().endsWith("package-info.java")) { 511 if (!ast.getFirstChild().hasChildren() && !isPrecededByJavadoc(ast)) { 512 log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText()); 513 } 514 } 515 else { 516 log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText()); 517 } 518 } 519 if (!hasEmptyLineAfter(ast)) { 520 log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText()); 521 } 522 } 523 524 /** 525 * Process Import. 526 * 527 * @param ast token 528 * @param nextToken next token 529 */ 530 private void processImport(DetailAST ast, DetailAST nextToken) { 531 if (nextToken.getType() != TokenTypes.IMPORT 532 && nextToken.getType() != TokenTypes.STATIC_IMPORT && !hasEmptyLineAfter(ast)) { 533 log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText()); 534 } 535 } 536 537 /** 538 * Process Variable. 539 * 540 * @param ast token 541 * @param nextToken next Token 542 */ 543 private void processVariableDef(DetailAST ast, DetailAST nextToken) { 544 if (isTypeField(ast) && !hasEmptyLineAfter(ast) 545 && isViolatingEmptyLineBetweenFieldsPolicy(nextToken)) { 546 log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, 547 nextToken.getText()); 548 } 549 } 550 551 /** 552 * Checks whether token placement violates policy of empty line between fields. 553 * 554 * @param detailAST token to be analyzed 555 * @return true if policy is violated and warning should be raised; false otherwise 556 */ 557 private boolean isViolatingEmptyLineBetweenFieldsPolicy(DetailAST detailAST) { 558 return detailAST.getType() != TokenTypes.RCURLY 559 && (!allowNoEmptyLineBetweenFields 560 || detailAST.getType() != TokenTypes.VARIABLE_DEF); 561 } 562 563 /** 564 * Checks if a token has empty two previous lines and multiple empty lines is not allowed. 565 * 566 * @param token DetailAST token 567 * @return true, if token has empty two lines before and allowMultipleEmptyLines is false 568 */ 569 private boolean hasNotAllowedTwoEmptyLinesBefore(DetailAST token) { 570 return !allowMultipleEmptyLines && hasEmptyLineBefore(token) 571 && isPrePreviousLineEmpty(token); 572 } 573 574 /** 575 * Check if group of comments located right before token has more than one previous empty line. 576 * 577 * @param token DetailAST token 578 */ 579 private void checkComments(DetailAST token) { 580 if (!allowMultipleEmptyLines) { 581 if (TOKEN_TYPES_WITHOUT_COMMENTS_TO_CHECK_INSIDE.contains(token.getType())) { 582 DetailAST previousNode = token.getPreviousSibling(); 583 while (isCommentInBeginningOfLine(previousNode)) { 584 if (hasEmptyLineBefore(previousNode) && isPrePreviousLineEmpty(previousNode)) { 585 log(previousNode, MSG_MULTIPLE_LINES, previousNode.getText()); 586 } 587 previousNode = previousNode.getPreviousSibling(); 588 } 589 } 590 else { 591 checkCommentsInsideToken(token); 592 } 593 } 594 } 595 596 /** 597 * Check if group of comments located at the start of token has more than one previous empty 598 * line. 599 * 600 * @param token DetailAST token 601 */ 602 private void checkCommentsInsideToken(DetailAST token) { 603 final List<DetailAST> childNodes = new LinkedList<>(); 604 DetailAST childNode = token.getLastChild(); 605 while (childNode != null) { 606 if (childNode.getType() == TokenTypes.MODIFIERS) { 607 for (DetailAST node = token.getFirstChild().getLastChild(); 608 node != null; 609 node = node.getPreviousSibling()) { 610 if (isCommentInBeginningOfLine(node)) { 611 childNodes.add(node); 612 } 613 } 614 } 615 else if (isCommentInBeginningOfLine(childNode)) { 616 childNodes.add(childNode); 617 } 618 childNode = childNode.getPreviousSibling(); 619 } 620 for (DetailAST node : childNodes) { 621 if (hasEmptyLineBefore(node) && isPrePreviousLineEmpty(node)) { 622 log(node, MSG_MULTIPLE_LINES, node.getText()); 623 } 624 } 625 } 626 627 /** 628 * Checks if a token has empty pre-previous line. 629 * 630 * @param token DetailAST token. 631 * @return true, if token has empty lines before. 632 */ 633 private boolean isPrePreviousLineEmpty(DetailAST token) { 634 boolean result = false; 635 final int lineNo = token.getLineNo(); 636 // 3 is the number of the pre-previous line because the numbering starts from zero. 637 final int number = 3; 638 if (lineNo >= number) { 639 final String prePreviousLine = getLines()[lineNo - number]; 640 result = CommonUtil.isBlank(prePreviousLine); 641 } 642 return result; 643 } 644 645 /** 646 * Checks if token have empty line after. 647 * 648 * @param token token. 649 * @return true if token have empty line after. 650 */ 651 private boolean hasEmptyLineAfter(DetailAST token) { 652 DetailAST lastToken = token.getLastChild().getLastChild(); 653 if (lastToken == null) { 654 lastToken = token.getLastChild(); 655 } 656 DetailAST nextToken = token.getNextSibling(); 657 if (isComment(nextToken)) { 658 nextToken = nextToken.getNextSibling(); 659 } 660 // Start of the next token 661 final int nextBegin = nextToken.getLineNo(); 662 // End of current token. 663 final int currentEnd = lastToken.getLineNo(); 664 return hasEmptyLine(currentEnd + 1, nextBegin - 1); 665 } 666 667 /** 668 * Finds comment in next sibling of given packageDef. 669 * 670 * @param packageDef token to check 671 * @return comment under the token 672 */ 673 private static Optional<DetailAST> findCommentUnder(DetailAST packageDef) { 674 return Optional.ofNullable(packageDef.getNextSibling()) 675 .map(sibling -> sibling.findFirstToken(TokenTypes.MODIFIERS)) 676 .map(DetailAST::getFirstChild) 677 .filter(EmptyLineSeparatorCheck::isComment) 678 .filter(comment -> comment.getLineNo() == packageDef.getLineNo() + 1); 679 } 680 681 /** 682 * Checks, whether there are empty lines within the specified line range. Line numbering is 683 * started from 1 for parameter values 684 * 685 * @param startLine number of the first line in the range 686 * @param endLine number of the second line in the range 687 * @return {@code true} if found any blank line within the range, {@code false} 688 * otherwise 689 */ 690 private boolean hasEmptyLine(int startLine, int endLine) { 691 // Initial value is false - blank line not found 692 boolean result = false; 693 final FileContents fileContents = getFileContents(); 694 for (int line = startLine; line <= endLine; line++) { 695 // Check, if the line is blank. Lines are numbered from 0, so subtract 1 696 if (fileContents.lineIsBlank(line - 1)) { 697 result = true; 698 break; 699 } 700 } 701 return result; 702 } 703 704 /** 705 * Checks if a token has a empty line before. 706 * 707 * @param token token. 708 * @return true, if token have empty line before. 709 */ 710 private boolean hasEmptyLineBefore(DetailAST token) { 711 boolean result = false; 712 final int lineNo = token.getLineNo(); 713 if (lineNo != 1) { 714 // [lineNo - 2] is the number of the previous line as the numbering starts from zero. 715 final String lineBefore = getLines()[lineNo - 2]; 716 result = CommonUtil.isBlank(lineBefore); 717 } 718 return result; 719 } 720 721 /** 722 * Check if token is comment, which starting in beginning of line. 723 * 724 * @param comment comment token for check. 725 * @return true, if token is comment, which starting in beginning of line. 726 */ 727 private boolean isCommentInBeginningOfLine(DetailAST comment) { 728 // [comment.getLineNo() - 1] is the number of the previous line as the numbering starts 729 // from zero. 730 boolean result = false; 731 if (comment != null) { 732 final String lineWithComment = getLines()[comment.getLineNo() - 1].trim(); 733 result = lineWithComment.startsWith("//") || lineWithComment.startsWith("/*"); 734 } 735 return result; 736 } 737 738 /** 739 * Check if token is preceded by javadoc comment. 740 * 741 * @param token token for check. 742 * @return true, if token is preceded by javadoc comment. 743 */ 744 private static boolean isPrecededByJavadoc(DetailAST token) { 745 boolean result = false; 746 final DetailAST previous = token.getPreviousSibling(); 747 if (previous.getType() == TokenTypes.BLOCK_COMMENT_BEGIN 748 && JavadocUtil.isJavadocComment(previous.getFirstChild().getText())) { 749 result = true; 750 } 751 return result; 752 } 753 754 /** 755 * Check if token is a comment. 756 * 757 * @param ast ast node 758 * @return true, if given ast is comment. 759 */ 760 private static boolean isComment(DetailAST ast) { 761 return ast.getType() == TokenTypes.SINGLE_LINE_COMMENT 762 || ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN; 763 } 764 765 /** 766 * If variable definition is a type field. 767 * 768 * @param variableDef variable definition. 769 * @return true variable definition is a type field. 770 */ 771 private static boolean isTypeField(DetailAST variableDef) { 772 final int parentType = variableDef.getParent().getParent().getType(); 773 return parentType == TokenTypes.CLASS_DEF; 774 } 775 776}