001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 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.javadoc; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024import java.util.List; 025import java.util.Locale; 026import java.util.Set; 027import java.util.regex.Pattern; 028 029import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser; 030import com.puppycrawl.tools.checkstyle.StatelessCheck; 031import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 032import com.puppycrawl.tools.checkstyle.api.DetailAST; 033import com.puppycrawl.tools.checkstyle.api.FileContents; 034import com.puppycrawl.tools.checkstyle.api.Scope; 035import com.puppycrawl.tools.checkstyle.api.TextBlock; 036import com.puppycrawl.tools.checkstyle.api.TokenTypes; 037import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 038import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 039import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 040 041/** 042 * <p> 043 * Validates Javadoc comments to help ensure they are well formed. 044 * </p> 045 * <p> 046 * The following checks are performed: 047 * </p> 048 * <ul> 049 * <li> 050 * Ensures the first sentence ends with proper punctuation 051 * (That is a period, question mark, or exclamation mark, by default). 052 * Javadoc automatically places the first sentence in the method summary 053 * table and index. Without proper punctuation the Javadoc may be malformed. 054 * All items eligible for the {@code {@inheritDoc}} tag are exempt from this 055 * requirement. 056 * </li> 057 * <li> 058 * Check text for Javadoc statements that do not have any description. 059 * This includes both completely empty Javadoc, and Javadoc with only tags 060 * such as {@code @param} and {@code @return}. 061 * </li> 062 * <li> 063 * Check text for incomplete HTML tags. Verifies that HTML tags have 064 * corresponding end tags and issues an "Unclosed HTML tag found:" error if not. 065 * An "Extra HTML tag found:" error is issued if an end tag is found without 066 * a previous open tag. 067 * </li> 068 * <li> 069 * Check that a package Javadoc comment is well-formed (as described above). 070 * </li> 071 * <li> 072 * Check for allowed HTML tags. The list of allowed HTML tags is 073 * "a", "abbr", "acronym", "address", "area", "b", "bdo", "big", "blockquote", 074 * "br", "caption", "cite", "code", "colgroup", "dd", "del", "dfn", "div", "dl", 075 * "dt", "em", "fieldset", "font", "h1", "h2", "h3", "h4", "h5", "h6", "hr", 076 * "i", "img", "ins", "kbd", "li", "ol", "p", "pre", "q", "samp", "small", 077 * "span", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", 078 * "thead", "tr", "tt", "u", "ul", "var". 079 * </li> 080 * </ul> 081 * <p> 082 * These checks were patterned after the checks made by the 083 * <a href="https://maven-doccheck.sourceforge.net">DocCheck</a> doclet 084 * available from Sun. Note: Original Sun's DocCheck tool does not exist anymore. 085 * </p> 086 * <ul> 087 * <li> 088 * Property {@code checkEmptyJavadoc} - Control whether to check if the Javadoc 089 * is missing a describing text. 090 * Type is {@code boolean}. 091 * Default value is {@code false}. 092 * </li> 093 * <li> 094 * Property {@code checkFirstSentence} - Control whether to check the first 095 * sentence for proper end of sentence. 096 * Type is {@code boolean}. 097 * Default value is {@code true}. 098 * </li> 099 * <li> 100 * Property {@code checkHtml} - Control whether to check for incomplete HTML tags. 101 * Type is {@code boolean}. 102 * Default value is {@code true}. 103 * </li> 104 * <li> 105 * Property {@code endOfSentenceFormat} - Specify the format for matching 106 * the end of a sentence. 107 * Type is {@code java.util.regex.Pattern}. 108 * Default value is {@code "([.?!][ \t\n\r\f<])|([.?!]$)"}. 109 * </li> 110 * <li> 111 * Property {@code excludeScope} - Specify the visibility scope where 112 * Javadoc comments are not checked. 113 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}. 114 * Default value is {@code null}. 115 * </li> 116 * <li> 117 * Property {@code scope} - Specify the visibility scope where Javadoc comments are checked. 118 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}. 119 * Default value is {@code private}. 120 * </li> 121 * <li> 122 * Property {@code tokens} - tokens to check 123 * Type is {@code java.lang.String[]}. 124 * Validation type is {@code tokenSet}. 125 * Default value is: 126 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF"> 127 * ANNOTATION_DEF</a>, 128 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF"> 129 * ANNOTATION_FIELD_DEF</a>, 130 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 131 * CLASS_DEF</a>, 132 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 133 * CTOR_DEF</a>, 134 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF"> 135 * ENUM_CONSTANT_DEF</a>, 136 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 137 * ENUM_DEF</a>, 138 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 139 * INTERFACE_DEF</a>, 140 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 141 * METHOD_DEF</a>, 142 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PACKAGE_DEF"> 143 * PACKAGE_DEF</a>, 144 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 145 * VARIABLE_DEF</a>, 146 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 147 * RECORD_DEF</a>, 148 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 149 * COMPACT_CTOR_DEF</a>. 150 * </li> 151 * </ul> 152 * <p> 153 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 154 * </p> 155 * <p> 156 * Violation Message Keys: 157 * </p> 158 * <ul> 159 * <li> 160 * {@code javadoc.empty} 161 * </li> 162 * <li> 163 * {@code javadoc.extraHtml} 164 * </li> 165 * <li> 166 * {@code javadoc.incompleteTag} 167 * </li> 168 * <li> 169 * {@code javadoc.noPeriod} 170 * </li> 171 * <li> 172 * {@code javadoc.unclosedHtml} 173 * </li> 174 * </ul> 175 * 176 * @since 3.2 177 */ 178@StatelessCheck 179public class JavadocStyleCheck 180 extends AbstractCheck { 181 182 /** Message property key for the Empty Javadoc message. */ 183 public static final String MSG_EMPTY = "javadoc.empty"; 184 185 /** Message property key for the No Javadoc end of Sentence Period message. */ 186 public static final String MSG_NO_PERIOD = "javadoc.noPeriod"; 187 188 /** Message property key for the Incomplete Tag message. */ 189 public static final String MSG_INCOMPLETE_TAG = "javadoc.incompleteTag"; 190 191 /** Message property key for the Unclosed HTML message. */ 192 public static final String MSG_UNCLOSED_HTML = JavadocDetailNodeParser.MSG_UNCLOSED_HTML_TAG; 193 194 /** Message property key for the Extra HTML message. */ 195 public static final String MSG_EXTRA_HTML = "javadoc.extraHtml"; 196 197 /** HTML tags that do not require a close tag. */ 198 private static final Set<String> SINGLE_TAGS = Set.of( 199 "br", "li", "dt", "dd", "hr", "img", "p", "td", "tr", "th" 200 ); 201 202 /** 203 * HTML tags that are allowed in java docs. 204 * From <a href="https://www.w3schools.com/tags/default.asp">w3schools</a>: 205 * <br> 206 * The forms and structure tags are not allowed 207 */ 208 private static final Set<String> ALLOWED_TAGS = Set.of( 209 "a", "abbr", "acronym", "address", "area", "b", "bdo", "big", 210 "blockquote", "br", "caption", "cite", "code", "colgroup", "dd", 211 "del", "dfn", "div", "dl", "dt", "em", "fieldset", "font", "h1", 212 "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "ins", "kbd", 213 "li", "ol", "p", "pre", "q", "samp", "small", "span", "strong", 214 "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", 215 "tr", "tt", "u", "ul", "var" 216 ); 217 218 /** Specify the visibility scope where Javadoc comments are checked. */ 219 private Scope scope = Scope.PRIVATE; 220 221 /** Specify the visibility scope where Javadoc comments are not checked. */ 222 private Scope excludeScope; 223 224 /** Specify the format for matching the end of a sentence. */ 225 private Pattern endOfSentenceFormat = Pattern.compile("([.?!][ \t\n\r\f<])|([.?!]$)"); 226 227 /** 228 * Control whether to check the first sentence for proper end of sentence. 229 */ 230 private boolean checkFirstSentence = true; 231 232 /** 233 * Control whether to check for incomplete HTML tags. 234 */ 235 private boolean checkHtml = true; 236 237 /** 238 * Control whether to check if the Javadoc is missing a describing text. 239 */ 240 private boolean checkEmptyJavadoc; 241 242 @Override 243 public int[] getDefaultTokens() { 244 return getAcceptableTokens(); 245 } 246 247 @Override 248 public int[] getAcceptableTokens() { 249 return new int[] { 250 TokenTypes.ANNOTATION_DEF, 251 TokenTypes.ANNOTATION_FIELD_DEF, 252 TokenTypes.CLASS_DEF, 253 TokenTypes.CTOR_DEF, 254 TokenTypes.ENUM_CONSTANT_DEF, 255 TokenTypes.ENUM_DEF, 256 TokenTypes.INTERFACE_DEF, 257 TokenTypes.METHOD_DEF, 258 TokenTypes.PACKAGE_DEF, 259 TokenTypes.VARIABLE_DEF, 260 TokenTypes.RECORD_DEF, 261 TokenTypes.COMPACT_CTOR_DEF, 262 }; 263 } 264 265 @Override 266 public int[] getRequiredTokens() { 267 return CommonUtil.EMPTY_INT_ARRAY; 268 } 269 270 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 271 @SuppressWarnings("deprecation") 272 @Override 273 public void visitToken(DetailAST ast) { 274 if (shouldCheck(ast)) { 275 final FileContents contents = getFileContents(); 276 // Need to start searching for the comment before the annotations 277 // that may exist. Even if annotations are not defined on the 278 // package, the ANNOTATIONS AST is defined. 279 final TextBlock textBlock = 280 contents.getJavadocBefore(ast.getFirstChild().getLineNo()); 281 282 checkComment(ast, textBlock); 283 } 284 } 285 286 /** 287 * Whether we should check this node. 288 * 289 * @param ast a given node. 290 * @return whether we should check a given node. 291 */ 292 private boolean shouldCheck(final DetailAST ast) { 293 boolean check = false; 294 295 if (ast.getType() == TokenTypes.PACKAGE_DEF) { 296 check = CheckUtil.isPackageInfo(getFilePath()); 297 } 298 else if (!ScopeUtil.isInCodeBlock(ast)) { 299 final Scope customScope = ScopeUtil.getScope(ast); 300 final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast); 301 302 check = customScope.isIn(scope) 303 && (surroundingScope == null || surroundingScope.isIn(scope)) 304 && (excludeScope == null 305 || !customScope.isIn(excludeScope) 306 || surroundingScope != null 307 && !surroundingScope.isIn(excludeScope)); 308 } 309 return check; 310 } 311 312 /** 313 * Performs the various checks against the Javadoc comment. 314 * 315 * @param ast the AST of the element being documented 316 * @param comment the source lines that make up the Javadoc comment. 317 * 318 * @see #checkFirstSentenceEnding(DetailAST, TextBlock) 319 * @see #checkHtmlTags(DetailAST, TextBlock) 320 */ 321 private void checkComment(final DetailAST ast, final TextBlock comment) { 322 if (comment != null) { 323 if (checkFirstSentence) { 324 checkFirstSentenceEnding(ast, comment); 325 } 326 327 if (checkHtml) { 328 checkHtmlTags(ast, comment); 329 } 330 331 if (checkEmptyJavadoc) { 332 checkJavadocIsNotEmpty(comment); 333 } 334 } 335 } 336 337 /** 338 * Checks that the first sentence ends with proper punctuation. This method 339 * uses a regular expression that checks for the presence of a period, 340 * question mark, or exclamation mark followed either by whitespace, an 341 * HTML element, or the end of string. This method ignores {_AT_inheritDoc} 342 * comments for TokenTypes that are valid for {_AT_inheritDoc}. 343 * 344 * @param ast the current node 345 * @param comment the source lines that make up the Javadoc comment. 346 */ 347 private void checkFirstSentenceEnding(final DetailAST ast, TextBlock comment) { 348 final String commentText = getCommentText(comment.getText()); 349 350 if (!commentText.isEmpty() 351 && !endOfSentenceFormat.matcher(commentText).find() 352 && !(commentText.startsWith("{@inheritDoc}") 353 && JavadocTagInfo.INHERIT_DOC.isValidOn(ast))) { 354 log(comment.getStartLineNo(), MSG_NO_PERIOD); 355 } 356 } 357 358 /** 359 * Checks that the Javadoc is not empty. 360 * 361 * @param comment the source lines that make up the Javadoc comment. 362 */ 363 private void checkJavadocIsNotEmpty(TextBlock comment) { 364 final String commentText = getCommentText(comment.getText()); 365 366 if (commentText.isEmpty()) { 367 log(comment.getStartLineNo(), MSG_EMPTY); 368 } 369 } 370 371 /** 372 * Returns the comment text from the Javadoc. 373 * 374 * @param comments the lines of Javadoc. 375 * @return a comment text String. 376 */ 377 private static String getCommentText(String... comments) { 378 final StringBuilder builder = new StringBuilder(1024); 379 for (final String line : comments) { 380 final int textStart = findTextStart(line); 381 382 if (textStart != -1) { 383 if (line.charAt(textStart) == '@') { 384 // we have found the tag section 385 break; 386 } 387 builder.append(line.substring(textStart)); 388 trimTail(builder); 389 builder.append('\n'); 390 } 391 } 392 393 return builder.toString().trim(); 394 } 395 396 /** 397 * Finds the index of the first non-whitespace character ignoring the 398 * Javadoc comment start and end strings (/** and */) as well as any 399 * leading asterisk. 400 * 401 * @param line the Javadoc comment line of text to scan. 402 * @return the int index relative to 0 for the start of text 403 * or -1 if not found. 404 */ 405 private static int findTextStart(String line) { 406 int textStart = -1; 407 int index = 0; 408 while (index < line.length()) { 409 if (!Character.isWhitespace(line.charAt(index))) { 410 if (line.regionMatches(index, "/**", 0, "/**".length())) { 411 index += 2; 412 } 413 else if (line.regionMatches(index, "*/", 0, 2)) { 414 index++; 415 } 416 else if (line.charAt(index) != '*') { 417 textStart = index; 418 break; 419 } 420 } 421 index++; 422 } 423 return textStart; 424 } 425 426 /** 427 * Trims any trailing whitespace or the end of Javadoc comment string. 428 * 429 * @param builder the StringBuilder to trim. 430 */ 431 private static void trimTail(StringBuilder builder) { 432 int index = builder.length() - 1; 433 while (true) { 434 if (Character.isWhitespace(builder.charAt(index))) { 435 builder.deleteCharAt(index); 436 } 437 else if (index > 0 && builder.charAt(index) == '/' 438 && builder.charAt(index - 1) == '*') { 439 builder.deleteCharAt(index); 440 builder.deleteCharAt(index - 1); 441 index--; 442 while (builder.charAt(index - 1) == '*') { 443 builder.deleteCharAt(index - 1); 444 index--; 445 } 446 } 447 else { 448 break; 449 } 450 index--; 451 } 452 } 453 454 /** 455 * Checks the comment for HTML tags that do not have a corresponding close 456 * tag or a close tag that has no previous open tag. This code was 457 * primarily copied from the DocCheck checkHtml method. 458 * 459 * @param ast the node with the Javadoc 460 * @param comment the {@code TextBlock} which represents 461 * the Javadoc comment. 462 * @noinspection MethodWithMultipleReturnPoints 463 * @noinspectionreason MethodWithMultipleReturnPoints - check and method are 464 * too complex to break apart 465 */ 466 // -@cs[ReturnCount] Too complex to break apart. 467 private void checkHtmlTags(final DetailAST ast, final TextBlock comment) { 468 final int lineNo = comment.getStartLineNo(); 469 final Deque<HtmlTag> htmlStack = new ArrayDeque<>(); 470 final String[] text = comment.getText(); 471 472 final TagParser parser = new TagParser(text, lineNo); 473 474 while (parser.hasNextTag()) { 475 final HtmlTag tag = parser.nextTag(); 476 477 if (tag.isIncompleteTag()) { 478 log(tag.getLineNo(), MSG_INCOMPLETE_TAG, 479 text[tag.getLineNo() - lineNo]); 480 return; 481 } 482 if (tag.isClosedTag()) { 483 // do nothing 484 continue; 485 } 486 if (tag.isCloseTag()) { 487 // We have found a close tag. 488 if (isExtraHtml(tag.getId(), htmlStack)) { 489 // No corresponding open tag was found on the stack. 490 log(tag.getLineNo(), 491 tag.getPosition(), 492 MSG_EXTRA_HTML, 493 tag.getText()); 494 } 495 else { 496 // See if there are any unclosed tags that were opened 497 // after this one. 498 checkUnclosedTags(htmlStack, tag.getId()); 499 } 500 } 501 else { 502 // We only push html tags that are allowed 503 if (isAllowedTag(tag)) { 504 htmlStack.push(tag); 505 } 506 } 507 } 508 509 // Identify any tags left on the stack. 510 // Skip multiples, like <b>...<b> 511 String lastFound = ""; 512 final List<String> typeParameters = CheckUtil.getTypeParameterNames(ast); 513 for (final HtmlTag htmlTag : htmlStack) { 514 if (!isSingleTag(htmlTag) 515 && !htmlTag.getId().equals(lastFound) 516 && !typeParameters.contains(htmlTag.getId())) { 517 log(htmlTag.getLineNo(), htmlTag.getPosition(), 518 MSG_UNCLOSED_HTML, htmlTag.getText()); 519 lastFound = htmlTag.getId(); 520 } 521 } 522 } 523 524 /** 525 * Checks to see if there are any unclosed tags on the stack. The token 526 * represents a html tag that has been closed and has a corresponding open 527 * tag on the stack. Any tags, except single tags, that were opened 528 * (pushed on the stack) after the token are missing a close. 529 * 530 * @param htmlStack the stack of opened HTML tags. 531 * @param token the current HTML tag name that has been closed. 532 */ 533 private void checkUnclosedTags(Deque<HtmlTag> htmlStack, String token) { 534 final Deque<HtmlTag> unclosedTags = new ArrayDeque<>(); 535 HtmlTag lastOpenTag = htmlStack.pop(); 536 while (!token.equalsIgnoreCase(lastOpenTag.getId())) { 537 // Find unclosed elements. Put them on a stack so the 538 // output order won't be back-to-front. 539 if (isSingleTag(lastOpenTag)) { 540 lastOpenTag = htmlStack.pop(); 541 } 542 else { 543 unclosedTags.push(lastOpenTag); 544 lastOpenTag = htmlStack.pop(); 545 } 546 } 547 548 // Output the unterminated tags, if any 549 // Skip multiples, like <b>..<b> 550 String lastFound = ""; 551 for (final HtmlTag htag : unclosedTags) { 552 lastOpenTag = htag; 553 if (lastOpenTag.getId().equals(lastFound)) { 554 continue; 555 } 556 lastFound = lastOpenTag.getId(); 557 log(lastOpenTag.getLineNo(), 558 lastOpenTag.getPosition(), 559 MSG_UNCLOSED_HTML, 560 lastOpenTag.getText()); 561 } 562 } 563 564 /** 565 * Determines if the HtmlTag is one which does not require a close tag. 566 * 567 * @param tag the HtmlTag to check. 568 * @return {@code true} if the HtmlTag is a single tag. 569 */ 570 private static boolean isSingleTag(HtmlTag tag) { 571 // If it's a singleton tag (<p>, <br>, etc.), ignore it 572 // Can't simply not put them on the stack, since singletons 573 // like <dt> and <dd> (unhappily) may either be terminated 574 // or not terminated. Both options are legal. 575 return SINGLE_TAGS.contains(tag.getId().toLowerCase(Locale.ENGLISH)); 576 } 577 578 /** 579 * Determines if the HtmlTag is one which is allowed in a javadoc. 580 * 581 * @param tag the HtmlTag to check. 582 * @return {@code true} if the HtmlTag is an allowed html tag. 583 */ 584 private static boolean isAllowedTag(HtmlTag tag) { 585 return ALLOWED_TAGS.contains(tag.getId().toLowerCase(Locale.ENGLISH)); 586 } 587 588 /** 589 * Determines if the given token is an extra HTML tag. This indicates that 590 * a close tag was found that does not have a corresponding open tag. 591 * 592 * @param token an HTML tag id for which a close was found. 593 * @param htmlStack a Stack of previous open HTML tags. 594 * @return {@code false} if a previous open tag was found 595 * for the token. 596 */ 597 private static boolean isExtraHtml(String token, Deque<HtmlTag> htmlStack) { 598 boolean isExtra = true; 599 for (final HtmlTag tag : htmlStack) { 600 // Loop, looking for tags that are closed. 601 // The loop is needed in case there are unclosed 602 // tags on the stack. In that case, the stack would 603 // not be empty, but this tag would still be extra. 604 if (token.equalsIgnoreCase(tag.getId())) { 605 isExtra = false; 606 break; 607 } 608 } 609 610 return isExtra; 611 } 612 613 /** 614 * Setter to specify the visibility scope where Javadoc comments are checked. 615 * 616 * @param scope a scope. 617 * @since 3.2 618 */ 619 public void setScope(Scope scope) { 620 this.scope = scope; 621 } 622 623 /** 624 * Setter to specify the visibility scope where Javadoc comments are not checked. 625 * 626 * @param excludeScope a scope. 627 * @since 3.4 628 */ 629 public void setExcludeScope(Scope excludeScope) { 630 this.excludeScope = excludeScope; 631 } 632 633 /** 634 * Setter to specify the format for matching the end of a sentence. 635 * 636 * @param pattern a pattern. 637 * @since 5.0 638 */ 639 public void setEndOfSentenceFormat(Pattern pattern) { 640 endOfSentenceFormat = pattern; 641 } 642 643 /** 644 * Setter to control whether to check the first sentence for proper end of sentence. 645 * 646 * @param flag {@code true} if the first sentence is to be checked 647 * @since 3.2 648 */ 649 public void setCheckFirstSentence(boolean flag) { 650 checkFirstSentence = flag; 651 } 652 653 /** 654 * Setter to control whether to check for incomplete HTML tags. 655 * 656 * @param flag {@code true} if HTML checking is to be performed. 657 * @since 3.2 658 */ 659 public void setCheckHtml(boolean flag) { 660 checkHtml = flag; 661 } 662 663 /** 664 * Setter to control whether to check if the Javadoc is missing a describing text. 665 * 666 * @param flag {@code true} if empty Javadoc checking should be done. 667 * @since 3.4 668 */ 669 public void setCheckEmptyJavadoc(boolean flag) { 670 checkEmptyJavadoc = flag; 671 } 672 673}