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 * &lt;module name=&quot;SuppressionXpathFilter&quot;&gt;
418 *   &lt;property name=&quot;file&quot; value=&quot;config/suppressions.xml&quot;/&gt;
419 *   &lt;property name=&quot;optional&quot; value=&quot;false&quot;/&gt;
420 * &lt;/module&gt;
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 * &lt;?xml version=&quot;1.0&quot;?&gt;
478 *
479 * &lt;!DOCTYPE suppressions PUBLIC
480 * &quot;-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2//EN&quot;
481 * &quot;https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd&quot;&gt;
482 *
483 * &lt;suppressions&gt;
484 *   &lt;suppress-xpath checks=&quot;CyclomaticComplexity&quot;
485 *   files=&quot;FileOne.java,FileTwo.java&quot;
486 *   query=&quot;//METHOD_DEF[./IDENT[@text='sayHelloWorld']]&quot;/&gt;
487 * &lt;/suppressions&gt;
488 * </pre>
489 * <p>
490 * Suppress checks for package definitions:
491 * </p>
492 * <pre>
493 * &lt;suppress-xpath checks=".*" query="/PACKAGE_DEF"/&gt;
494 * </pre>
495 * <p>
496 * Suppress checks for parent element of the first variable definition:
497 * </p>
498 * <pre>
499 * &lt;suppress-xpath checks=".*" query="(//VARIABLE_DEF)[1]/.."/&gt;
500 * </pre>
501 * <p>
502 * Suppress checks for elements which are either class definitions, either method definitions.
503 * </p>
504 * <pre>
505 * &lt;suppress-xpath checks=".*" query="//CLASS_DEF | //METHOD_DEF"/&gt;
506 * </pre>
507 * <p>
508 * Suppress checks for certain methods:
509 * </p>
510 * <pre>
511 * &lt;suppress-xpath checks=&quot;.*&quot; query=&quot;//METHOD_DEF[./IDENT[@text='getSomeVar'
512 *           or @text='setSomeVar']]&quot;/&gt;
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 * &lt;suppress-xpath checks=&quot;.*&quot; query=&quot;/CLASS_DEF[@text='TestClass']
520 *           //METHOD_DEF[./IDENT[@text='testMethod']]
521 *           //VARIABLE_DEF[./IDENT[@text='testVariable']]&quot;/&gt;
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 * &lt;suppress-xpath checks=&quot;LeftCurly&quot; query=&quot;/CLASS_DEF[./IDENT[@text='Main']]//*
529 *           | //METHOD_DEF[./IDENT[@text='calculate']]/*&quot;/&gt;
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 * &lt;suppress-xpath checks=&quot;RequireThis&quot;
537 *      query=&quot;/CLASS_DEF[./IDENT[@text='InputTest']]
538 *           //METHOD_DEF[./IDENT[@text='changeAge']]//ASSIGN/IDENT[@text='age']&quot;/&gt;
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 * &lt;suppress-xpath checks="IllegalThrows" query="//LITERAL_THROWS
559 *           /IDENT[@text='RuntimeException' and
560 *           ./ancestor::METHOD_DEF[./IDENT[@text='throwsMethod']]]"/&gt;
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 * &lt;suppress-xpath checks=".*" query="//METHOD_DEF[./IDENT[@text='legacyMethod']]
580 *           /descendant-or-self::node()"/&gt;
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 -&gt; CLASS_DEF [1:0]
597 * |--MODIFIERS -&gt; MODIFIERS [1:0]
598 * |   `--LITERAL_PUBLIC -&gt; public [1:0]
599 * |--LITERAL_CLASS -&gt; class [1:7]
600 * |--IDENT -&gt; InputTest [1:13]
601 * `--OBJBLOCK -&gt; OBJBLOCK [1:23]
602 * |--LCURLY -&gt; { [1:23]
603 * |--VARIABLE_DEF -&gt; VARIABLE_DEF [2:4]
604 * |   |--MODIFIERS -&gt; MODIFIERS [2:4]
605 * |   |   `--LITERAL_PRIVATE -&gt; private [2:4]
606 * |   |--TYPE -&gt; TYPE [2:12]
607 * |   |   `--LITERAL_INT -&gt; int [2:12]
608 * |   |--IDENT -&gt; wordCount [2:16]
609 * |   |--ASSIGN -&gt; = [2:26]
610 * |   |   `--EXPR -&gt; EXPR [2:28]
611 * |   |       `--NUM_INT -&gt; 11 [2:28]
612 * |   `--SEMI -&gt; ; [2:30]
613 * `--RCURLY -&gt; } [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 * &lt;suppress-xpath checks="." query="//VARIABLE_DEF[
625 *             ./IDENT[@text='wordCount']]"/&gt;
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 * &lt;suppress-xpath checks="." query="//VARIABLE_DEF[.//NUM_INT[@text=11]]"/&gt;
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 *             &#64;Generated("first") // should not be suppressed
645 *             public void test1() {
646 *             }
647 *
648 *             &#64;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 -&gt; CLASS_DEF [1:0]
659 * |--MODIFIERS -&gt; MODIFIERS [1:0]
660 * |   `--LITERAL_PUBLIC -&gt; public [1:0]
661 * |--LITERAL_CLASS -&gt; class [1:7]
662 * |--IDENT -&gt; InputTest [1:13]
663 * `--OBJBLOCK -&gt; OBJBLOCK [1:23]
664 * |--LCURLY -&gt; { [1:23]
665 * |--METHOD_DEF -&gt; METHOD_DEF [2:4]
666 * |   |--MODIFIERS -&gt; MODIFIERS [2:4]
667 * |   |   |--ANNOTATION -&gt; ANNOTATION [2:4]
668 * |   |   |   |--AT -&gt; @ [2:4]
669 * |   |   |   |--IDENT -&gt; Generated [2:5]
670 * |   |   |   |--LPAREN -&gt; ( [2:14]
671 * |   |   |   |--EXPR -&gt; EXPR [2:15]
672 * |   |   |   |   `--STRING_LITERAL -&gt; "first" [2:15]
673 * |   |   |   `--RPAREN -&gt; ) [2:22]
674 * |   |   `--LITERAL_PUBLIC -&gt; public [3:4]
675 * |   |--TYPE -&gt; TYPE [3:11]
676 * |   |   `--LITERAL_VOID -&gt; void [3:11]
677 * |   |--IDENT -&gt; test1 [3:16]
678 * |   |--LPAREN -&gt; ( [3:21]
679 * |   |--PARAMETERS -&gt; PARAMETERS [3:22]
680 * |   |--RPAREN -&gt; ) [3:22]
681 * |   `--SLIST -&gt; { [3:24]
682 * |       `--RCURLY -&gt; } [4:4]
683 * |--METHOD_DEF -&gt; METHOD_DEF [6:4]
684 * |   |--MODIFIERS -&gt; MODIFIERS [6:4]
685 * |   |   |--ANNOTATION -&gt; ANNOTATION [6:4]
686 * |   |   |   |--AT -&gt; @ [6:4]
687 * |   |   |   |--IDENT -&gt; Generated [6:5]
688 * |   |   |   |--LPAREN -&gt; ( [6:14]
689 * |   |   |   |--EXPR -&gt; EXPR [6:15]
690 * |   |   |   |   `--STRING_LITERAL -&gt; "second" [6:15]
691 * |   |   |   `--RPAREN -&gt; ) [6:23]
692 * |   |   `--LITERAL_PUBLIC -&gt; public [7:4]
693 * |   |--TYPE -&gt; TYPE [7:11]
694 * |   |   `--LITERAL_VOID -&gt; void [7:11]
695 * |   |--IDENT -&gt; test2 [7:16]
696 * |   |--LPAREN -&gt; ( [7:21]
697 * |   |--PARAMETERS -&gt; PARAMETERS [7:22]
698 * |   |--RPAREN -&gt; ) [7:22]
699 * |   `--SLIST -&gt; { [7:24]
700 * |       `--RCURLY -&gt; } [8:4]
701 * `--RCURLY -&gt; } [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 * &lt;suppress-xpath checks="." query="//METHOD_DEF[
709 *             .//ANNOTATION/IDENT[@text='Generated']]"/&gt;
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 * &lt;suppress-xpath checks="." query="//METHOD_DEF[.//ANNOTATION[
721 *             ./IDENT[@text='Generated'] and ./EXPR/STRING_LITERAL[@text='second']]]"/&gt;
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}