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