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.LinkedList; 024import java.util.List; 025import java.util.Optional; 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 /** Allow no empty line between fields. */ 095 private boolean allowNoEmptyLineBetweenFields; 096 097 /** Allow multiple empty lines between class members. */ 098 private boolean allowMultipleEmptyLines = true; 099 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}