001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.filters; 021 022import java.io.File; 023import java.io.IOException; 024import java.nio.charset.StandardCharsets; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.List; 028import java.util.Objects; 029import java.util.Optional; 030import java.util.regex.Matcher; 031import java.util.regex.Pattern; 032import java.util.regex.PatternSyntaxException; 033 034import com.puppycrawl.tools.checkstyle.PropertyType; 035import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 036import com.puppycrawl.tools.checkstyle.api.AuditEvent; 037import com.puppycrawl.tools.checkstyle.api.AutomaticBean; 038import com.puppycrawl.tools.checkstyle.api.FileText; 039import com.puppycrawl.tools.checkstyle.api.Filter; 040import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 041 042/** 043 * <p> 044 * Filter {@code SuppressWithPlainTextCommentFilter} uses plain text to suppress 045 * audit events. The filter can be used only to suppress audit events received 046 * from the checks which implement FileSetCheck interface. In other words, the 047 * checks which have Checker as a parent module. The filter knows nothing about 048 * AST, it treats only plain text comments and extracts the information required 049 * for suppression from the plain text comments. Currently, the filter supports 050 * only single-line comments. 051 * </p> 052 * <p> 053 * Please, be aware of the fact that, it is not recommended to use the filter 054 * for Java code anymore, however you still are able to use it to suppress audit 055 * events received from the checks which implement FileSetCheck interface. 056 * </p> 057 * <p> 058 * Rationale: Sometimes there are legitimate reasons for violating a check. 059 * When this is a matter of the code in question and not personal preference, 060 * the best place to override the policy is in the code itself. Semi-structured 061 * comments can be associated with the check. This is sometimes superior to 062 * a separate suppressions file, which must be kept up-to-date as the source 063 * file is edited. 064 * </p> 065 * <p> 066 * Note that the suppression comment should be put before the violation. 067 * You can use more than one suppression comment each on separate line. 068 * </p> 069 * <p> 070 * Properties {@code offCommentFormat} and {@code onCommentFormat} must have equal 071 * <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Matcher.html#groupCount()"> 072 * paren counts</a>. 073 * </p> 074 * <p> 075 * SuppressionWithPlainTextCommentFilter can suppress Checks that have Treewalker or 076 * Checker as parent module. 077 * </p> 078 * <ul> 079 * <li> 080 * Property {@code offCommentFormat} - Specify comment pattern to trigger filter 081 * to begin suppression. 082 * Type is {@code java.util.regex.Pattern}. 083 * Default value is {@code "// CHECKSTYLE:OFF"}. 084 * </li> 085 * <li> 086 * Property {@code onCommentFormat} - Specify comment pattern to trigger filter 087 * to end suppression. 088 * Type is {@code java.util.regex.Pattern}. 089 * Default value is {@code "// CHECKSTYLE:ON"}. 090 * </li> 091 * <li> 092 * Property {@code checkFormat} - Specify check pattern to suppress. 093 * Type is {@code java.util.regex.Pattern}. 094 * Default value is {@code ".*"}. 095 * </li> 096 * <li> 097 * Property {@code messageFormat} - Specify message pattern to suppress. 098 * Type is {@code java.util.regex.Pattern}. 099 * Default value is {@code null}. 100 * </li> 101 * <li> 102 * Property {@code idFormat} - Specify check ID pattern to suppress. 103 * Type is {@code java.util.regex.Pattern}. 104 * Default value is {@code null}. 105 * </li> 106 * </ul> 107 * <p> 108 * To configure a filter to suppress audit events between a comment containing 109 * {@code CHECKSTYLE:OFF} and a comment containing {@code CHECKSTYLE:ON}: 110 * </p> 111 * <pre> 112 * <module name="Checker"> 113 * ... 114 * <module name="SuppressWithPlainTextCommentFilter"/> 115 * ... 116 * </module> 117 * </pre> 118 * <p> 119 * To configure a filter to suppress audit events between a comment containing 120 * line {@code BEGIN GENERATED CONTENT} and a comment containing line 121 * {@code END GENERATED CONTENT}(Checker is configured to check only properties files): 122 * </p> 123 * <pre> 124 * <module name="Checker"> 125 * <property name="fileExtensions" value="properties"/> 126 * 127 * <module name="SuppressWithPlainTextCommentFilter"> 128 * <property name="offCommentFormat" value="BEGIN GENERATED CONTENT"/> 129 * <property name="onCommentFormat" value="END GENERATED CONTENT"/> 130 * </module> 131 * 132 * </module> 133 * </pre> 134 * <pre> 135 * //BEGIN GENERATED CONTENT 136 * my.property=value1 // No violation events will be reported 137 * my.property=value2 // No violation events will be reported 138 * //END GENERATED CONTENT 139 * . . . 140 * </pre> 141 * <p> 142 * To configure a filter so that {@code -- stop tab check} and {@code -- resume tab check} 143 * marks allowed tab positions (Checker is configured to check only sql files): 144 * </p> 145 * <pre> 146 * <module name="Checker"> 147 * <property name="fileExtensions" value="sql"/> 148 * 149 * <module name="SuppressWithPlainTextCommentFilter"> 150 * <property name="offCommentFormat" value="stop tab check"/> 151 * <property name="onCommentFormat" value="resume tab check"/> 152 * <property name="checkFormat" value="FileTabCharacterCheck"/> 153 * </module> 154 * 155 * </module> 156 * </pre> 157 * <pre> 158 * -- stop tab check 159 * SELECT * FROM users // won't warn here if there is a tab character on line 160 * -- resume tab check 161 * SELECT 1 // will warn here if there is a tab character on line 162 * </pre> 163 * <p> 164 * To configure a filter so that name of suppressed check mentioned in comment 165 * {@code CSOFF: <i>regexp</i>} and {@code CSON: <i>regexp</i>} mark a matching 166 * check (Checker is configured to check only xml files): 167 * </p> 168 * <pre> 169 * <module name="Checker"> 170 * <property name="fileExtensions" value="xml"/> 171 * 172 * <module name="SuppressWithPlainTextCommentFilter"> 173 * <property name="offCommentFormat" value="CSOFF\: ([\w\|]+)"/> 174 * <property name="onCommentFormat" value="CSON\: ([\w\|]+)"/> 175 * <property name="checkFormat" value="$1"/> 176 * </module> 177 * 178 * </module> 179 * </pre> 180 * <pre> 181 * // CSOFF: RegexpSinglelineCheck 182 * // RegexpSingleline check won't warn any lines below here if the line matches regexp 183 * <condition property="checkstyle.ant.skip"> 184 * <isset property="checkstyle.ant.skip"/> 185 * </condition> 186 * // CSON: RegexpSinglelineCheck 187 * // RegexpSingleline check will warn below here if the line matches regexp 188 * <property name="checkstyle.pattern.todo" value="NOTHingWillMatCH_-"/> 189 * </pre> 190 * <p> 191 * To configure a filter to suppress all audit events between a comment containing 192 * {@code CHECKSTYLE_OFF: ALMOST_ALL} and a comment containing {@code CHECKSTYLE_OFF: ALMOST_ALL} 193 * except for the <em>EqualsHashCode</em> check (Checker is configured to check only java files): 194 * </p> 195 * <pre> 196 * <module name="Checker"> 197 * <property name="fileExtensions" value="java"/> 198 * 199 * <module name="SuppressWithPlainTextCommentFilter"> 200 * <property name="offCommentFormat" 201 * value="CHECKSTYLE_OFF: ALMOST_ALL"/> 202 * <property name="onCommentFormat" 203 * value="CHECKSTYLE_ON: ALMOST_ALL"/> 204 * <property name="checkFormat" 205 * value="^((?!(FileTabCharacterCheck)).)*$"/> 206 * </module> 207 * 208 * </module> 209 * </pre> 210 * <pre> 211 * // CHECKSTYLE_OFF: ALMOST_ALL 212 * public static final int array []; 213 * private String [] strArray; 214 * // CHECKSTYLE_ON: ALMOST_ALL 215 * private int array1 []; 216 * </pre> 217 * <p> 218 * To configure a filter to suppress Check's violation message <b>which matches 219 * specified message in messageFormat</b>(so suppression will not be only by 220 * Check's name, but also by message text, as the same Check can report violations 221 * with different message format) between a comment containing {@code stop} and 222 * comment containing {@code resume}: 223 * </p> 224 * <pre> 225 * <module name="Checker"> 226 * <module name="SuppressWithPlainTextCommentFilter"> 227 * <property name="offCommentFormat" value="stop"/> 228 * <property name="onCommentFormat" value="resume"/> 229 * <property name="checkFormat" value="FileTabCharacterCheck"/> 230 * <property name="messageFormat" 231 * value="^File contains tab characters (this is the first instance)\.$"/> 232 * </module> 233 * </module> 234 * </pre> 235 * <p> 236 * It is possible to specify an ID of checks, so that it can be leveraged by the 237 * SuppressWithPlainTextCommentFilter to skip validations. The following examples 238 * show how to skip validations near code that is surrounded with 239 * {@code -- CSOFF <ID> (reason)} and {@code -- CSON <ID>}, 240 * where ID is the ID of checks you want to suppress. 241 * </p> 242 * <p> 243 * Examples of Checkstyle checks configuration: 244 * </p> 245 * <pre> 246 * <module name="RegexpSinglelineJava"> 247 * <property name="id" value="count"/> 248 * <property name="format" value="^.*COUNT(*).*$"/> 249 * <property name="message" 250 * value="Don't use COUNT(*), use COUNT(1) instead."/> 251 * </module> 252 * 253 * <module name="RegexpSinglelineJava"> 254 * <property name="id" value="join"/> 255 * <property name="format" value="^.*JOIN\s.+\s(ON|USING)$"/> 256 * <property name="message" 257 * value="Don't use JOIN, use sub-select instead."/> 258 * </module> 259 * </pre> 260 * <p> 261 * Example of SuppressWithPlainTextCommentFilter configuration (checkFormat which 262 * is set to '$1' points that ID of the checks is in the first group of offCommentFormat 263 * and onCommentFormat regular expressions): 264 * </p> 265 * <pre> 266 * <module name="Checker"> 267 * <property name="fileExtensions" value="sql"/> 268 * 269 * <module name="SuppressWithPlainTextCommentFilter"> 270 * <property name="offCommentFormat" value="CSOFF (\w+) \(\w+\)"/> 271 * <property name="onCommentFormat" value="CSON (\w+)"/> 272 * <property name="idFormat" value="$1"/> 273 * </module> 274 * 275 * </module> 276 * </pre> 277 * <pre> 278 * -- CSOFF join (it is ok to use join here for performance reasons) 279 * SELECT name, job_name 280 * FROM users AS u 281 * JOIN jobs AS j ON u.job_id = j.id 282 * -- CSON join 283 * 284 * -- CSOFF count (test query execution plan) 285 * EXPLAIN SELECT COUNT(*) FROM restaurants 286 * -- CSON count 287 * </pre> 288 * <p> 289 * Example of how to configure the check to suppress more than one check 290 * (Checker is configured to check only sql files). 291 * </p> 292 * <pre> 293 * <module name="Checker"> 294 * <property name="fileExtensions" value="sql"/> 295 * 296 * <module name="SuppressWithPlainTextCommentFilter"> 297 * <property name="offCommentFormat" value="@cs-\: ([\w\|]+)"/> 298 * <property name="checkFormat" value="$1"/> 299 * </module> 300 * 301 * </module> 302 * </pre> 303 * <pre> 304 * -- @cs-: RegexpSinglelineCheck 305 * -- @cs-: FileTabCharacterCheck 306 * CREATE TABLE STATION ( 307 * ID INTEGER PRIMARY KEY, 308 * CITY CHAR(20), 309 * STATE CHAR(2), 310 * LAT_N REAL, 311 * LONG_W REAL); 312 * </pre> 313 * <p> 314 * Parent is {@code com.puppycrawl.tools.checkstyle.Checker} 315 * </p> 316 * 317 * @since 8.6 318 */ 319public class SuppressWithPlainTextCommentFilter extends AutomaticBean implements Filter { 320 321 /** Comment format which turns checkstyle reporting off. */ 322 private static final String DEFAULT_OFF_FORMAT = "// CHECKSTYLE:OFF"; 323 324 /** Comment format which turns checkstyle reporting on. */ 325 private static final String DEFAULT_ON_FORMAT = "// CHECKSTYLE:ON"; 326 327 /** Default check format to suppress. By default, the filter suppress all checks. */ 328 private static final String DEFAULT_CHECK_FORMAT = ".*"; 329 330 /** Specify comment pattern to trigger filter to begin suppression. */ 331 private Pattern offCommentFormat = CommonUtil.createPattern(DEFAULT_OFF_FORMAT); 332 333 /** Specify comment pattern to trigger filter to end suppression. */ 334 private Pattern onCommentFormat = CommonUtil.createPattern(DEFAULT_ON_FORMAT); 335 336 /** Specify check pattern to suppress. */ 337 @XdocsPropertyType(PropertyType.PATTERN) 338 private String checkFormat = DEFAULT_CHECK_FORMAT; 339 340 /** Specify message pattern to suppress. */ 341 @XdocsPropertyType(PropertyType.PATTERN) 342 private String messageFormat; 343 344 /** Specify check ID pattern to suppress. */ 345 @XdocsPropertyType(PropertyType.PATTERN) 346 private String idFormat; 347 348 /** 349 * Setter to specify comment pattern to trigger filter to begin suppression. 350 * 351 * @param pattern off comment format pattern. 352 */ 353 public final void setOffCommentFormat(Pattern pattern) { 354 offCommentFormat = pattern; 355 } 356 357 /** 358 * Setter to specify comment pattern to trigger filter to end suppression. 359 * 360 * @param pattern on comment format pattern. 361 */ 362 public final void setOnCommentFormat(Pattern pattern) { 363 onCommentFormat = pattern; 364 } 365 366 /** 367 * Setter to specify check pattern to suppress. 368 * 369 * @param format pattern for check format. 370 */ 371 public final void setCheckFormat(String format) { 372 checkFormat = format; 373 } 374 375 /** 376 * Setter to specify message pattern to suppress. 377 * 378 * @param format pattern for message format. 379 */ 380 public final void setMessageFormat(String format) { 381 messageFormat = format; 382 } 383 384 /** 385 * Setter to specify check ID pattern to suppress. 386 * 387 * @param format pattern for check ID format 388 */ 389 public final void setIdFormat(String format) { 390 idFormat = format; 391 } 392 393 @Override 394 public boolean accept(AuditEvent event) { 395 boolean accepted = true; 396 if (event.getViolation() != null) { 397 final FileText fileText = getFileText(event.getFileName()); 398 if (fileText != null) { 399 final List<Suppression> suppressions = getSuppressions(fileText); 400 accepted = getNearestSuppression(suppressions, event) == null; 401 } 402 } 403 return accepted; 404 } 405 406 @Override 407 protected void finishLocalSetup() { 408 // No code by default 409 } 410 411 /** 412 * Returns {@link FileText} instance created based on the given file name. 413 * 414 * @param fileName the name of the file. 415 * @return {@link FileText} instance. 416 * @throws IllegalStateException if the file could not be read. 417 */ 418 private static FileText getFileText(String fileName) { 419 final File file = new File(fileName); 420 FileText result = null; 421 422 // some violations can be on a directory, instead of a file 423 if (!file.isDirectory()) { 424 try { 425 result = new FileText(file, StandardCharsets.UTF_8.name()); 426 } 427 catch (IOException ex) { 428 throw new IllegalStateException("Cannot read source file: " + fileName, ex); 429 } 430 } 431 432 return result; 433 } 434 435 /** 436 * Returns the list of {@link Suppression} instances retrieved from the given {@link FileText}. 437 * 438 * @param fileText {@link FileText} instance. 439 * @return list of {@link Suppression} instances. 440 */ 441 private List<Suppression> getSuppressions(FileText fileText) { 442 final List<Suppression> suppressions = new ArrayList<>(); 443 for (int lineNo = 0; lineNo < fileText.size(); lineNo++) { 444 final Optional<Suppression> suppression = getSuppression(fileText, lineNo); 445 suppression.ifPresent(suppressions::add); 446 } 447 return suppressions; 448 } 449 450 /** 451 * Tries to extract the suppression from the given line. 452 * 453 * @param fileText {@link FileText} instance. 454 * @param lineNo line number. 455 * @return {@link Optional} of {@link Suppression}. 456 */ 457 private Optional<Suppression> getSuppression(FileText fileText, int lineNo) { 458 final String line = fileText.get(lineNo); 459 final Matcher onCommentMatcher = onCommentFormat.matcher(line); 460 final Matcher offCommentMatcher = offCommentFormat.matcher(line); 461 462 Suppression suppression = null; 463 if (onCommentMatcher.find()) { 464 suppression = new Suppression(onCommentMatcher.group(0), 465 lineNo + 1, onCommentMatcher.start(), SuppressionType.ON, this); 466 } 467 if (offCommentMatcher.find()) { 468 suppression = new Suppression(offCommentMatcher.group(0), 469 lineNo + 1, offCommentMatcher.start(), SuppressionType.OFF, this); 470 } 471 472 return Optional.ofNullable(suppression); 473 } 474 475 /** 476 * Finds the nearest {@link Suppression} instance which can suppress 477 * the given {@link AuditEvent}. The nearest suppression is the suppression which scope 478 * is before the line and column of the event. 479 * 480 * @param suppressions collection of {@link Suppression} instances. 481 * @param event {@link AuditEvent} instance. 482 * @return {@link Suppression} instance. 483 */ 484 private static Suppression getNearestSuppression(Collection<Suppression> suppressions, 485 AuditEvent event) { 486 return suppressions 487 .stream() 488 .filter(suppression -> suppression.isMatch(event)) 489 .reduce((first, second) -> second) 490 .filter(suppression -> suppression.suppressionType != SuppressionType.ON) 491 .orElse(null); 492 } 493 494 /** Enum which represents the type of the suppression. */ 495 private enum SuppressionType { 496 497 /** On suppression type. */ 498 ON, 499 /** Off suppression type. */ 500 OFF, 501 502 } 503 504 /** The class which represents the suppression. */ 505 private static final class Suppression { 506 507 /** The regexp which is used to match the event source.*/ 508 private final Pattern eventSourceRegexp; 509 /** The regexp which is used to match the event message.*/ 510 private final Pattern eventMessageRegexp; 511 /** The regexp which is used to match the event ID.*/ 512 private final Pattern eventIdRegexp; 513 514 /** Suppression text.*/ 515 private final String text; 516 /** Suppression line.*/ 517 private final int lineNo; 518 /** Suppression column number.*/ 519 private final int columnNo; 520 /** Suppression type. */ 521 private final SuppressionType suppressionType; 522 523 /** 524 * Creates new suppression instance. 525 * 526 * @param text suppression text. 527 * @param lineNo suppression line number. 528 * @param columnNo suppression column number. 529 * @param suppressionType suppression type. 530 * @param filter the {@link SuppressWithPlainTextCommentFilter} with the context. 531 * @throws IllegalArgumentException if there is an error in the filter regex syntax. 532 */ 533 /* package */ Suppression( 534 String text, 535 int lineNo, 536 int columnNo, 537 SuppressionType suppressionType, 538 SuppressWithPlainTextCommentFilter filter 539 ) { 540 this.text = text; 541 this.lineNo = lineNo; 542 this.columnNo = columnNo; 543 this.suppressionType = suppressionType; 544 545 final Pattern commentFormat; 546 if (this.suppressionType == SuppressionType.ON) { 547 commentFormat = filter.onCommentFormat; 548 } 549 else { 550 commentFormat = filter.offCommentFormat; 551 } 552 553 // Expand regexp for check and message 554 // Does not intern Patterns with Utils.getPattern() 555 String format = ""; 556 try { 557 format = CommonUtil.fillTemplateWithStringsByRegexp( 558 filter.checkFormat, text, commentFormat); 559 eventSourceRegexp = Pattern.compile(format); 560 if (filter.messageFormat == null) { 561 eventMessageRegexp = null; 562 } 563 else { 564 format = CommonUtil.fillTemplateWithStringsByRegexp( 565 filter.messageFormat, text, commentFormat); 566 eventMessageRegexp = Pattern.compile(format); 567 } 568 if (filter.idFormat == null) { 569 eventIdRegexp = null; 570 } 571 else { 572 format = CommonUtil.fillTemplateWithStringsByRegexp( 573 filter.idFormat, text, commentFormat); 574 eventIdRegexp = Pattern.compile(format); 575 } 576 } 577 catch (final PatternSyntaxException ex) { 578 throw new IllegalArgumentException( 579 "unable to parse expanded comment " + format, ex); 580 } 581 } 582 583 /** 584 * Indicates whether some other object is "equal to" this one. 585 * Suppression on enumeration is needed so code stays consistent. 586 * 587 * @noinspection EqualsCalledOnEnumConstant 588 */ 589 @Override 590 public boolean equals(Object other) { 591 if (this == other) { 592 return true; 593 } 594 if (other == null || getClass() != other.getClass()) { 595 return false; 596 } 597 final Suppression suppression = (Suppression) other; 598 return Objects.equals(lineNo, suppression.lineNo) 599 && Objects.equals(columnNo, suppression.columnNo) 600 && Objects.equals(suppressionType, suppression.suppressionType) 601 && Objects.equals(text, suppression.text) 602 && Objects.equals(eventSourceRegexp, suppression.eventSourceRegexp) 603 && Objects.equals(eventMessageRegexp, suppression.eventMessageRegexp) 604 && Objects.equals(eventIdRegexp, suppression.eventIdRegexp); 605 } 606 607 @Override 608 public int hashCode() { 609 return Objects.hash( 610 text, lineNo, columnNo, suppressionType, eventSourceRegexp, eventMessageRegexp, 611 eventIdRegexp); 612 } 613 614 /** 615 * Checks whether the suppression matches the given {@link AuditEvent}. 616 * 617 * @param event {@link AuditEvent} instance. 618 * @return true if the suppression matches {@link AuditEvent}. 619 */ 620 private boolean isMatch(AuditEvent event) { 621 return isInScopeOfSuppression(event) 622 && isCheckMatch(event) 623 && isIdMatch(event) 624 && isMessageMatch(event); 625 } 626 627 /** 628 * Checks whether {@link AuditEvent} is in the scope of the suppression. 629 * 630 * @param event {@link AuditEvent} instance. 631 * @return true if {@link AuditEvent} is in the scope of the suppression. 632 */ 633 private boolean isInScopeOfSuppression(AuditEvent event) { 634 return lineNo <= event.getLine(); 635 } 636 637 /** 638 * Checks whether {@link AuditEvent} source name matches the check format. 639 * 640 * @param event {@link AuditEvent} instance. 641 * @return true if the {@link AuditEvent} source name matches the check format. 642 */ 643 private boolean isCheckMatch(AuditEvent event) { 644 final Matcher checkMatcher = eventSourceRegexp.matcher(event.getSourceName()); 645 return checkMatcher.find(); 646 } 647 648 /** 649 * Checks whether the {@link AuditEvent} module ID matches the ID format. 650 * 651 * @param event {@link AuditEvent} instance. 652 * @return true if the {@link AuditEvent} module ID matches the ID format. 653 */ 654 private boolean isIdMatch(AuditEvent event) { 655 boolean match = true; 656 if (eventIdRegexp != null) { 657 if (event.getModuleId() == null) { 658 match = false; 659 } 660 else { 661 final Matcher idMatcher = eventIdRegexp.matcher(event.getModuleId()); 662 match = idMatcher.find(); 663 } 664 } 665 return match; 666 } 667 668 /** 669 * Checks whether the {@link AuditEvent} message matches the message format. 670 * 671 * @param event {@link AuditEvent} instance. 672 * @return true if the {@link AuditEvent} message matches the message format. 673 */ 674 private boolean isMessageMatch(AuditEvent event) { 675 boolean match = true; 676 if (eventMessageRegexp != null) { 677 final Matcher messageMatcher = eventMessageRegexp.matcher(event.getMessage()); 678 match = messageMatcher.find(); 679 } 680 return match; 681 } 682 } 683 684}