001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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.util.Collections; 023import java.util.HashSet; 024import java.util.Objects; 025import java.util.Set; 026 027import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent; 028import com.puppycrawl.tools.checkstyle.TreeWalkerFilter; 029import com.puppycrawl.tools.checkstyle.api.AutomaticBean; 030import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 031import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 032import com.puppycrawl.tools.checkstyle.utils.FilterUtil; 033 034/** 035 * <p> 036 * Filter {@code SuppressionXpathFilter} works as 037 * <a href="https://checkstyle.org/config_filters.html#SuppressionFilter">SuppressionFilter</a>. 038 * Additionally, filter processes {@code suppress-xpath} elements, 039 * which contains xpath-expressions. Xpath-expressions are queries for 040 * suppressed nodes inside the AST tree. 041 * </p> 042 * <p> 043 * Currently, filter supports the following checks: 044 * </p> 045 * <ul> 046 * <li> 047 * AbstractClassName 048 * </li> 049 * <li> 050 * AnonInnerLength 051 * </li> 052 * <li> 053 * ArrayTypeStyle 054 * </li> 055 * <li> 056 * AvoidInlineConditionals 057 * </li> 058 * <li> 059 * AvoidNestedBlocks 060 * </li> 061 * <li> 062 * BooleanExpressionComplexity 063 * </li> 064 * <li> 065 * CatchParameterName 066 * </li> 067 * <li> 068 * ClassDataAbstractionCoupling 069 * </li> 070 * <li> 071 * ClassFanOutComplexity 072 * </li> 073 * <li> 074 * ClassMemberImpliedModifier 075 * </li> 076 * <li> 077 * ClassTypeParameterName 078 * </li> 079 * <li> 080 * ConstantName 081 * </li> 082 * <li> 083 * CovariantEquals 084 * </li> 085 * <li> 086 * CyclomaticComplexity 087 * </li> 088 * <li> 089 * DeclarationOrder 090 * </li> 091 * <li> 092 * DefaultComesLast 093 * </li> 094 * <li> 095 * DescendantToken 096 * </li> 097 * <li> 098 * DesignForExtension 099 * </li> 100 * <li> 101 * EmptyBlock 102 * </li> 103 * <li> 104 * EmptyForInitializerPad 105 * </li> 106 * <li> 107 * EmptyForIteratorPad 108 * </li> 109 * <li> 110 * EmptyStatement 111 * </li> 112 * <li> 113 * EqualsAvoidNull 114 * </li> 115 * <li> 116 * EqualsHashCode 117 * </li> 118 * <li> 119 * ExecutableStatementCount 120 * </li> 121 * <li> 122 * ExplicitInitialization 123 * </li> 124 * <li> 125 * FallThrough 126 * </li> 127 * <li> 128 * FinalLocalVariable 129 * </li> 130 * <li> 131 * FinalParameters 132 * </li> 133 * <li> 134 * GenericWhitespace 135 * </li> 136 * <li> 137 * HiddenField 138 * </li> 139 * <li> 140 * HideUtilityClassConstructor 141 * </li> 142 * <li> 143 * IllegalInstantiation 144 * </li> 145 * <li> 146 * IllegalToken 147 * </li> 148 * <li> 149 * IllegalTokenText 150 * </li> 151 * <li> 152 * IllegalType 153 * </li> 154 * <li> 155 * InnerAssignment 156 * </li> 157 * <li> 158 * InnerTypeLast 159 * </li> 160 * <li> 161 * InterfaceTypeParameterName 162 * </li> 163 * <li> 164 * JavadocVariable 165 * </li> 166 * <li> 167 * JavaNCSS 168 * </li> 169 * <li> 170 * IllegalImport 171 * </li> 172 * <li> 173 * IllegalThrows 174 * </li> 175 * <li> 176 * ImportControl 177 * </li> 178 * <li> 179 * LeftCurly 180 * </li> 181 * <li> 182 * LocalFinalVariableName 183 * </li> 184 * <li> 185 * LocalVariableName 186 * </li> 187 * <li> 188 * MagicNumber 189 * </li> 190 * <li> 191 * MemberName 192 * </li> 193 * <li> 194 * MethodLength 195 * </li> 196 * <li> 197 * MethodName 198 * </li> 199 * <li> 200 * MethodParamPad 201 * </li> 202 * <li> 203 * MethodTypeParameterName 204 * </li> 205 * <li> 206 * ModifiedControlVariable 207 * </li> 208 * <li> 209 * ModifierOrder 210 * </li> 211 * <li> 212 * MultipleStringLiterals 213 * </li> 214 * <li> 215 * MultipleVariableDeclarations 216 * </li> 217 * <li> 218 * MutableException 219 * </li> 220 * <li> 221 * NestedForDepth 222 * </li> 223 * <li> 224 * NestedIfDepth 225 * </li> 226 * <li> 227 * NestedTryDepth 228 * </li> 229 * <li> 230 * NoWhitespaceAfter 231 * </li> 232 * <li> 233 * NoWhitespaceBefore 234 * </li> 235 * <li> 236 * NPathComplexity 237 * </li> 238 * <li> 239 * OneStatementPerLine 240 * </li> 241 * <li> 242 * OperatorWrap 243 * </li> 244 * <li> 245 * OuterTypeNumber 246 * </li> 247 * <li> 248 * PackageName 249 * </li> 250 * <li> 251 * ParameterAssignment 252 * </li> 253 * <li> 254 * ParameterName 255 * </li> 256 * <li> 257 * ParameterNumber 258 * </li> 259 * <li> 260 * ParenPad 261 * </li> 262 * <li> 263 * RedundantImport 264 * </li> 265 * <li> 266 * RedundantModifier 267 * </li> 268 * <li> 269 * RequireThis 270 * </li> 271 * <li> 272 * ReturnCount 273 * </li> 274 * <li> 275 * RightCurly 276 * </li> 277 * <li> 278 * SeparatorWrap 279 * </li> 280 * <li> 281 * SimplifyBooleanExpression 282 * </li> 283 * <li> 284 * SimplifyBooleanReturn 285 * </li> 286 * <li> 287 * SingleSpaceSeparator 288 * </li> 289 * <li> 290 * StaticVariableName 291 * </li> 292 * <li> 293 * StringLiteralEquality 294 * </li> 295 * <li> 296 * SuperClone 297 * </li> 298 * <li> 299 * SuperFinalize 300 * </li> 301 * <li> 302 * SuppressWarnings 303 * </li> 304 * <li> 305 * ThrowsCount 306 * </li> 307 * <li> 308 * TypecastParenPad 309 * </li> 310 * <li> 311 * TypeName 312 * </li> 313 * <li> 314 * UnnecessarySemicolonInEnumeration 315 * </li> 316 * <li> 317 * UnnecessarySemicolonInTryWithResources 318 * </li> 319 * <li> 320 * UnusedImports 321 * </li> 322 * <li> 323 * UpperEll 324 * </li> 325 * <li> 326 * VisibilityModifier 327 * </li> 328 * <li> 329 * WhitespaceAfter 330 * </li> 331 * <li> 332 * WhitespaceAround 333 * </li> 334 * </ul> 335 * <p> 336 * Note, that support for other Checks will be available after resolving 337 * <a href="https://github.com/checkstyle/checkstyle/issues/4830">issue 4830</a>. 338 * </p> 339 * <p> 340 * Currently, filter supports the following xpath axes: 341 * </p> 342 * <ul> 343 * <li> 344 * ancestor 345 * </li> 346 * <li> 347 * ancestor-or-self 348 * </li> 349 * <li> 350 * attribute 351 * </li> 352 * <li> 353 * child 354 * </li> 355 * <li> 356 * descendant 357 * </li> 358 * <li> 359 * descendant-or-self 360 * </li> 361 * <li> 362 * following 363 * </li> 364 * <li> 365 * following-sibling 366 * </li> 367 * <li> 368 * parent 369 * </li> 370 * <li> 371 * preceding 372 * </li> 373 * <li> 374 * preceding-sibling 375 * </li> 376 * <li> 377 * self 378 * </li> 379 * </ul> 380 * <p> 381 * You can use the command line helper tool to generate xpath suppressions based on your 382 * configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a> 383 * for more details. 384 * </p> 385 * <p> 386 * The suppression file location is checked in following order: 387 * </p> 388 * <ol> 389 * <li> 390 * as a filesystem location 391 * </li> 392 * <li> 393 * if no file found, and the location starts with either {@code http://} or {@code https://}, 394 * then it is interpreted as a URL 395 * </li> 396 * <li> 397 * if no file found, then passed to the {@code ClassLoader.getResource()} method. 398 * </li> 399 * </ol> 400 * <ul> 401 * <li> 402 * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file. 403 * Default value is {@code null}. 404 * </li> 405 * <li> 406 * Property {@code optional} - Control what to do when the file is not existing. 407 * If optional is set to false the file must exist, or else it ends with error. 408 * On the other hand if optional is true and file is not found, the filter accepts all audit events. 409 * Default value is {@code false}. 410 * </li> 411 * </ul> 412 * <p> 413 * For example, the following configuration fragment directs the Checker to use a 414 * {@code SuppressionXpathFilter} with suppressions file {@code config/suppressions.xml}: 415 * </p> 416 * <pre> 417 * <module name="SuppressionXpathFilter"> 418 * <property name="file" value="config/suppressions.xml"/> 419 * <property name="optional" value="false"/> 420 * </module> 421 * </pre> 422 * <p> 423 * A <a href="https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd"><em> 424 * suppressions XML document</em></a> 425 * contains a set of {@code suppress} and {@code suppress-xpath} elements, 426 * where each {@code suppress-xpath} element can have the following attributes: 427 * </p> 428 * <ul> 429 * <li> 430 * {@code files} - a <a href="https://checkstyle.org/property_types.html#regexp">Regular Expression</a> 431 * matched against the file name associated with an audit event. It is optional. 432 * </li> 433 * <li> 434 * {@code checks} - a <a href="https://checkstyle.org/property_types.html#regexp">Regular Expression</a> 435 * matched against the name of the check associated with an audit event. 436 * Optional as long as {@code id} or {@code message} is specified. 437 * </li> 438 * <li> 439 * {@code message} - a <a href="https://checkstyle.org/property_types.html#regexp">Regular Expression</a> 440 * matched against the message of the check associated with an audit event. 441 * Optional as long as {@code checks} or {@code id} is specified. 442 * </li> 443 * <li> 444 * {@code id} - a <a href="https://checkstyle.org/property_types.html#string">string</a> matched against 445 * the ID of the check associated with an audit event. 446 * Optional as long as {@code checks} or {@code message} is specified. 447 * </li> 448 * <li> 449 * {@code query} - a <a href="https://checkstyle.org/property_types.html#string">string</a> xpath query. It is optional. 450 * </li> 451 * </ul> 452 * <p> 453 * Each audit event is checked against each {@code suppress} and {@code suppress-xpath} element. 454 * It is suppressed if all specified attributes match against the audit event. 455 * </p> 456 * <p> 457 * ATTENTION: filtering by message is dependant on runtime locale. 458 * If project is running in different languages it is better to avoid filtering by message. 459 * </p> 460 * <p> 461 * The following suppressions XML document directs a {@code SuppressionXpathFilter} to reject 462 * {@code CyclomaticComplexity} violations for all methods with name <i>sayHelloWorld</i> inside 463 * <i>FileOne</i> and <i>FileTwo</i> files: 464 * </p> 465 * <p> 466 * Currently, xpath queries support one type of attribute {@code @text}. {@code @text} - 467 * addresses to the text value of the node. For example: variable name, annotation name, 468 * text content and etc. Only the following token types support {@code @text} attribute: 469 * {@code TokenTypes.IDENT}, {@code TokenTypes.STRING_LITERAL}, {@code TokenTypes.CHAR_LITERAL}, 470 * {@code TokenTypes.NUM_LONG}, {@code TokenTypes.NUM_INT}, {@code TokenTypes.NUM_DOUBLE}, 471 * {@code TokenTypes.NUM_FLOAT}. 472 * These token types were selected because only their text values are different 473 * in content from token type and represent text value from file and can be used 474 * in xpath queries for more accurate results. Other token types always have constant values. 475 * </p> 476 * <pre> 477 * <?xml version="1.0"?> 478 * 479 * <!DOCTYPE suppressions PUBLIC 480 * "-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2//EN" 481 * "https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd"> 482 * 483 * <suppressions> 484 * <suppress-xpath checks="CyclomaticComplexity" 485 * files="FileOne.java,FileTwo.java" 486 * query="//METHOD_DEF[./IDENT[@text='sayHelloWorld']]"/> 487 * </suppressions> 488 * </pre> 489 * <p> 490 * Suppress checks for package definitions: 491 * </p> 492 * <pre> 493 * <suppress-xpath checks=".*" query="/PACKAGE_DEF"/> 494 * </pre> 495 * <p> 496 * Suppress checks for parent element of the first variable definition: 497 * </p> 498 * <pre> 499 * <suppress-xpath checks=".*" query="(//VARIABLE_DEF)[1]/.."/> 500 * </pre> 501 * <p> 502 * Suppress checks for elements which are either class definitions, either method definitions. 503 * </p> 504 * <pre> 505 * <suppress-xpath checks=".*" query="//CLASS_DEF | //METHOD_DEF"/> 506 * </pre> 507 * <p> 508 * Suppress checks for certain methods: 509 * </p> 510 * <pre> 511 * <suppress-xpath checks=".*" query="//METHOD_DEF[./IDENT[@text='getSomeVar' 512 * or @text='setSomeVar']]"/> 513 * </pre> 514 * <p> 515 * Suppress checks for variable <i>testVariable</i> inside <i>testMethod</i> 516 * method inside <i>TestClass</i> class. 517 * </p> 518 * <pre> 519 * <suppress-xpath checks=".*" query="/CLASS_DEF[@text='TestClass'] 520 * //METHOD_DEF[./IDENT[@text='testMethod']] 521 * //VARIABLE_DEF[./IDENT[@text='testVariable']]"/> 522 * </pre> 523 * <p> 524 * In the following sample, violations for {@code LeftCurly} check will be suppressed 525 * for classes with name <i>Main</i> or for methods with name <i>calculate</i>. 526 * </p> 527 * <pre> 528 * <suppress-xpath checks="LeftCurly" query="/CLASS_DEF[./IDENT[@text='Main']]//* 529 * | //METHOD_DEF[./IDENT[@text='calculate']]/*"/> 530 * </pre> 531 * <p> 532 * The following example demonstrates how to suppress {@code RequireThis} violations 533 * for variable <i>age</i> inside <i>changeAge</i> method. 534 * </p> 535 * <pre> 536 * <suppress-xpath checks="RequireThis" 537 * query="/CLASS_DEF[./IDENT[@text='InputTest']] 538 * //METHOD_DEF[./IDENT[@text='changeAge']]//ASSIGN/IDENT[@text='age']"/> 539 * </pre> 540 * <pre> 541 * public class InputTest { 542 * private int age = 23; 543 * 544 * public void changeAge() { 545 * age = 24; //violation will be suppressed 546 * } 547 * } 548 * </pre> 549 * <p> 550 * Suppress {@code IllegalThrows} violations only for methods with name <i>throwsMethod</i> 551 * and only for {@code RuntimeException} exceptions. Double colon is used for axis iterations. 552 * In the following example {@code ancestor} axis is used to iterate all ancestor nodes 553 * of the current node with type {@code METHOD_DEF} and name <i>throwsMethod</i>. 554 * Please read more about xpath axes at <a href="https://www.w3schools.com/xml/xpath_axes.asp"> 555 * W3Schools Xpath Axes</a>. 556 * </p> 557 * <pre> 558 * <suppress-xpath checks="IllegalThrows" query="//LITERAL_THROWS 559 * /IDENT[@text='RuntimeException' and 560 * ./ancestor::METHOD_DEF[./IDENT[@text='throwsMethod']]]"/> 561 * </pre> 562 * <pre> 563 * public class InputTest { 564 * public void throwsMethod() throws RuntimeException { // violation will be suppressed 565 * } 566 * 567 * public void sampleMethod() throws RuntimeException { // will throw violation here 568 * } 569 * } 570 * </pre> 571 * <p> 572 * The following sample demonstrates how to suppress all violations for method 573 * itself and all descendants. {@code descendant-or-self} axis iterates through 574 * current node and all children nodes at any level. Keyword {@code node()} 575 * selects node elements. Please read more about xpath syntax at 576 * <a href="https://www.w3schools.com/xml/xpath_syntax.asp">W3Schools Xpath Syntax</a>. 577 * </p> 578 * <pre> 579 * <suppress-xpath checks=".*" query="//METHOD_DEF[./IDENT[@text='legacyMethod']] 580 * /descendant-or-self::node()"/> 581 * </pre> 582 * <p> 583 * Some elements can be suppressed in different ways. For example, to suppress 584 * violation on variable {@code wordCount} in following code: 585 * </p> 586 * <pre> 587 * public class InputTest { 588 * private int wordCount = 11; 589 * } 590 * </pre> 591 * <p> 592 * You need to look at AST of such code by our CLI tool: 593 * </p> 594 * <pre> 595 * $ java -jar checkstyle-X.XX-all.jar -t InputTest.java 596 * CLASS_DEF -> CLASS_DEF [1:0] 597 * |--MODIFIERS -> MODIFIERS [1:0] 598 * | `--LITERAL_PUBLIC -> public [1:0] 599 * |--LITERAL_CLASS -> class [1:7] 600 * |--IDENT -> InputTest [1:13] 601 * `--OBJBLOCK -> OBJBLOCK [1:23] 602 * |--LCURLY -> { [1:23] 603 * |--VARIABLE_DEF -> VARIABLE_DEF [2:4] 604 * | |--MODIFIERS -> MODIFIERS [2:4] 605 * | | `--LITERAL_PRIVATE -> private [2:4] 606 * | |--TYPE -> TYPE [2:12] 607 * | | `--LITERAL_INT -> int [2:12] 608 * | |--IDENT -> wordCount [2:16] 609 * | |--ASSIGN -> = [2:26] 610 * | | `--EXPR -> EXPR [2:28] 611 * | | `--NUM_INT -> 11 [2:28] 612 * | `--SEMI -> ; [2:30] 613 * `--RCURLY -> } [3:0] 614 * </pre> 615 * <p> 616 * The easiest way is to suppress by variable name. As you can see {@code VARIABLE_DEF} 617 * node refers to variable declaration statement and has child node with token type 618 * {@code IDENT} which is used for storing class, method, variable names. 619 * </p> 620 * <p> 621 * The following example demonstrates how variable can be queried by its name: 622 * </p> 623 * <pre> 624 * <suppress-xpath checks="." query="//VARIABLE_DEF[ 625 * ./IDENT[@text='wordCount']]"/> 626 * </pre> 627 * <p> 628 * Another way is to suppress by variable value. Again, if you look at the printed 629 * AST tree above, you will notice that one of the grandchildren of {@code VARIABLE_DEF} 630 * node is responsible for storing variable value -{@code NUM_INT} with value <b>11</b>. 631 * </p> 632 * <p> 633 * The following example demonstrates how variable can be queried by its value, 634 * same approach applies to {@code String, char, float, double, int, long} data types: 635 * </p> 636 * <pre> 637 * <suppress-xpath checks="." query="//VARIABLE_DEF[.//NUM_INT[@text=11]]"/> 638 * </pre> 639 * <p> 640 * Next example is about suppressing method with certain annotation by its name and element value. 641 * </p> 642 * <pre> 643 * public class InputTest { 644 * @Generated("first") // should not be suppressed 645 * public void test1() { 646 * } 647 * 648 * @Generated("second") // should be suppressed 649 * public void test2() { 650 * } 651 * } 652 * </pre> 653 * <p> 654 * First of all we need to look at AST tree printed by our CLI tool: 655 * </p> 656 * <pre> 657 * $ java -jar checkstyle-X.XX-all.jar -t InputTest.java 658 * CLASS_DEF -> CLASS_DEF [1:0] 659 * |--MODIFIERS -> MODIFIERS [1:0] 660 * | `--LITERAL_PUBLIC -> public [1:0] 661 * |--LITERAL_CLASS -> class [1:7] 662 * |--IDENT -> InputTest [1:13] 663 * `--OBJBLOCK -> OBJBLOCK [1:23] 664 * |--LCURLY -> { [1:23] 665 * |--METHOD_DEF -> METHOD_DEF [2:4] 666 * | |--MODIFIERS -> MODIFIERS [2:4] 667 * | | |--ANNOTATION -> ANNOTATION [2:4] 668 * | | | |--AT -> @ [2:4] 669 * | | | |--IDENT -> Generated [2:5] 670 * | | | |--LPAREN -> ( [2:14] 671 * | | | |--EXPR -> EXPR [2:15] 672 * | | | | `--STRING_LITERAL -> "first" [2:15] 673 * | | | `--RPAREN -> ) [2:22] 674 * | | `--LITERAL_PUBLIC -> public [3:4] 675 * | |--TYPE -> TYPE [3:11] 676 * | | `--LITERAL_VOID -> void [3:11] 677 * | |--IDENT -> test1 [3:16] 678 * | |--LPAREN -> ( [3:21] 679 * | |--PARAMETERS -> PARAMETERS [3:22] 680 * | |--RPAREN -> ) [3:22] 681 * | `--SLIST -> { [3:24] 682 * | `--RCURLY -> } [4:4] 683 * |--METHOD_DEF -> METHOD_DEF [6:4] 684 * | |--MODIFIERS -> MODIFIERS [6:4] 685 * | | |--ANNOTATION -> ANNOTATION [6:4] 686 * | | | |--AT -> @ [6:4] 687 * | | | |--IDENT -> Generated [6:5] 688 * | | | |--LPAREN -> ( [6:14] 689 * | | | |--EXPR -> EXPR [6:15] 690 * | | | | `--STRING_LITERAL -> "second" [6:15] 691 * | | | `--RPAREN -> ) [6:23] 692 * | | `--LITERAL_PUBLIC -> public [7:4] 693 * | |--TYPE -> TYPE [7:11] 694 * | | `--LITERAL_VOID -> void [7:11] 695 * | |--IDENT -> test2 [7:16] 696 * | |--LPAREN -> ( [7:21] 697 * | |--PARAMETERS -> PARAMETERS [7:22] 698 * | |--RPAREN -> ) [7:22] 699 * | `--SLIST -> { [7:24] 700 * | `--RCURLY -> } [8:4] 701 * `--RCURLY -> } [9:0] 702 * </pre> 703 * <p> 704 * AST node {@code ANNOTATION -> ANNOTATION [6:4]} has direct child 705 * {@code IDENT -> Generated [6:5]}, therefore can be queried by {@code IDENT} value: 706 * </p> 707 * <pre> 708 * <suppress-xpath checks="." query="//METHOD_DEF[ 709 * .//ANNOTATION/IDENT[@text='Generated']]"/> 710 * </pre> 711 * <p> 712 * The problem with query above that it will suppress violations for all methods 713 * with annotation {@code @Generated}. In order to suppress methods with 714 * {@code @Generated("second")} annotations only, you need to look at AST tree again. 715 * Value of the {@code ANNOTATION} node is stored inside sub-node with token type 716 * {@code STRING_LITERAL}. Use the following query to suppress methods with 717 * {@code @Generated("second")} annotation: 718 * </p> 719 * <pre> 720 * <suppress-xpath checks="." query="//METHOD_DEF[.//ANNOTATION[ 721 * ./IDENT[@text='Generated'] and ./EXPR/STRING_LITERAL[@text='second']]]"/> 722 * </pre> 723 * 724 * @since 8.6 725 * @noinspection NonFinalFieldReferenceInEquals, NonFinalFieldReferencedInHashCode 726 */ 727public class SuppressionXpathFilter extends AutomaticBean implements 728 TreeWalkerFilter, ExternalResourceHolder { 729 730 /** Specify the location of the <em>suppressions XML document</em> file. */ 731 private String file; 732 /** 733 * Control what to do when the file is not existing. 734 * If optional is set to false the file must exist, or else it ends with error. 735 * On the other hand if optional is true and file is not found, 736 * the filter accepts all audit events. 737 */ 738 private boolean optional; 739 /** Set of individual xpath suppresses. */ 740 private Set<TreeWalkerFilter> filters = new HashSet<>(); 741 742 /** 743 * Setter to specify the location of the <em>suppressions XML document</em> file. 744 * @param fileName name of the suppressions file. 745 */ 746 public void setFile(String fileName) { 747 file = fileName; 748 } 749 750 /** 751 * Setter to control what to do when the file is not existing. 752 * If optional is set to false the file must exist, or else it ends with error. 753 * On the other hand if optional is true and file is not found, 754 * the filter accepts all audit events. 755 * @param optional tells if config file existence is optional. 756 */ 757 public void setOptional(boolean optional) { 758 this.optional = optional; 759 } 760 761 @Override 762 public boolean equals(Object obj) { 763 if (this == obj) { 764 return true; 765 } 766 if (obj == null || getClass() != obj.getClass()) { 767 return false; 768 } 769 final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj; 770 return Objects.equals(filters, suppressionXpathFilter.filters); 771 } 772 773 @Override 774 public int hashCode() { 775 return Objects.hash(filters); 776 } 777 778 @Override 779 public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) { 780 boolean result = true; 781 for (TreeWalkerFilter filter : filters) { 782 if (!filter.accept(treeWalkerAuditEvent)) { 783 result = false; 784 break; 785 } 786 } 787 return result; 788 } 789 790 @Override 791 public Set<String> getExternalResourceLocations() { 792 return Collections.singleton(file); 793 } 794 795 @Override 796 protected void finishLocalSetup() throws CheckstyleException { 797 if (file != null) { 798 if (optional) { 799 if (FilterUtil.isFileExists(file)) { 800 filters = SuppressionsLoader.loadXpathSuppressions(file); 801 } 802 else { 803 filters = new HashSet<>(); 804 } 805 } 806 else { 807 filters = SuppressionsLoader.loadXpathSuppressions(file); 808 } 809 } 810 } 811 812}