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