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