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