001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.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 does not support the following checks:
044 * </p>
045 * <ul id="SuppressionXpathFilter_IncompatibleChecks">
046 * <li>
047 * AnnotationLocation
048 * </li>
049 * <li>
050 * AnnotationOnSameLine
051 * </li>
052 * <li>
053 * AnnotationUseStyle
054 * </li>
055 * <li>
056 * ArrayTrailingComma
057 * </li>
058 * <li>
059 * AvoidEscapedUnicodeCharacters
060 * </li>
061 * <li>
062 * AvoidStarImport
063 * </li>
064 * <li>
065 * AvoidStaticImport
066 * </li>
067 * <li>
068 * CommentsIndentation
069 * </li>
070 * <li>
071 * CustomImportOrder
072 * </li>
073 * <li>
074 * EmptyCatchBlock
075 * </li>
076 * <li>
077 * EmptyLineSeparator
078 * </li>
079 * <li>
080 * FinalClass
081 * </li>
082 * <li>
083 * IllegalCatch
084 * </li>
085 * <li>
086 * ImportOrder
087 * </li>
088 * <li>
089 * Indentation
090 * </li>
091 * <li>
092 * InterfaceIsType
093 * </li>
094 * <li>
095 * InterfaceMemberImpliedModifier
096 * </li>
097 * <li>
098 * InvalidJavadocPosition
099 * </li>
100 * <li>
101 * JavadocContentLocation
102 * </li>
103 * <li>
104 * JavadocMethod
105 * </li>
106 * <li>
107 * JavadocStyle
108 * </li>
109 * <li>
110 * JavadocType
111 * </li>
112 * <li>
113 * LambdaParameterName
114 * </li>
115 * <li>
116 * MethodCount
117 * </li>
118 * <li>
119 * MissingCtor
120 * </li>
121 * <li>
122 * MissingJavadocMethod
123 * </li>
124 * <li>
125 * MissingJavadocPackage
126 * </li>
127 * <li>
128 * MissingJavadocType
129 * </li>
130 * <li>
131 * MissingOverride
132 * </li>
133 * <li>
134 * MissingSwitchDefault
135 * </li>
136 * <li>
137 * NeedBraces
138 * </li>
139 * <li>
140 * NoClone
141 * </li>
142 * <li>
143 * NoFinalizer
144 * </li>
145 * <li>
146 * NoLineWrap
147 * </li>
148 * <li>
149 * OneTopLevelClass
150 * </li>
151 * <li>
152 * OuterTypeFilename
153 * </li>
154 * <li>
155 * OverloadMethodsDeclarationOrder
156 * </li>
157 * <li>
158 * PackageAnnotation
159 * </li>
160 * <li>
161 * PackageDeclaration
162 * </li>
163 * <li>
164 * Regexp
165 * </li>
166 * <li>
167 * RegexpSinglelineJava
168 * </li>
169 * <li>
170 * SuppressWarningsHolder
171 * </li>
172 * <li>
173 * TodoComment
174 * </li>
175 * <li>
176 * TrailingComment
177 * </li>
178 * <li>
179 * UncommentedMain
180 * </li>
181 * <li>
182 * UnnecessaryParentheses
183 * </li>
184 * <li>
185 * VariableDeclarationUsageDistance
186 * </li>
187 * <li>
188 * WriteTag
189 * </li>
190 * </ul>
191 * <p>
192 * Also, the filter does not support Javadoc checks:
193 * </p>
194 * <ul id="SuppressionXpathFilter_JavadocChecks">
195 * <li>
196 * AtclauseOrder
197 * </li>
198 * <li>
199 * JavadocBlockTagLocation
200 * </li>
201 * <li>
202 * JavadocParagraph
203 * </li>
204 * <li>
205 * JavadocTagContinuationIndentation
206 * </li>
207 * <li>
208 * MissingDeprecated
209 * </li>
210 * <li>
211 * NonEmptyAtclauseDescription
212 * </li>
213 * <li>
214 * SingleLineJavadoc
215 * </li>
216 * <li>
217 * SummaryJavadoc
218 * </li>
219 * </ul>
220 * <p>
221 * Note, that support for these Checks will be available after resolving issues
222 * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a> and
223 * <a href="https://github.com/checkstyle/checkstyle/issues/5777">#5777</a>.
224 * </p>
225 * <p>
226 * Currently, filter supports the following xpath axes:
227 * </p>
228 * <ul>
229 * <li>
230 * ancestor
231 * </li>
232 * <li>
233 * ancestor-or-self
234 * </li>
235 * <li>
236 * attribute
237 * </li>
238 * <li>
239 * child
240 * </li>
241 * <li>
242 * descendant
243 * </li>
244 * <li>
245 * descendant-or-self
246 * </li>
247 * <li>
248 * following
249 * </li>
250 * <li>
251 * following-sibling
252 * </li>
253 * <li>
254 * parent
255 * </li>
256 * <li>
257 * preceding
258 * </li>
259 * <li>
260 * preceding-sibling
261 * </li>
262 * <li>
263 * self
264 * </li>
265 * </ul>
266 * <p>
267 * You can use the command line helper tool to generate xpath suppressions based on your
268 * configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a>
269 * for more details.
270 * </p>
271 * <p>
272 * The suppression file location is checked in following order:
273 * </p>
274 * <ol>
275 * <li>
276 * as a filesystem location
277 * </li>
278 * <li>
279 * if no file found, and the location starts with either {@code http://} or {@code https://},
280 * then it is interpreted as a URL
281 * </li>
282 * <li>
283 * if no file found, then passed to the {@code ClassLoader.getResource()} method.
284 * </li>
285 * </ol>
286 * <ul>
287 * <li>
288 * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
289 * Default value is {@code null}.
290 * </li>
291 * <li>
292 * Property {@code optional} - Control what to do when the file is not existing.
293 * If optional is set to false the file must exist, or else it ends with error.
294 * On the other hand if optional is true and file is not found, the filter accepts all audit events.
295 * Default value is {@code false}.
296 * </li>
297 * </ul>
298 * <p>
299 * For example, the following configuration fragment directs the Checker to use a
300 * {@code SuppressionXpathFilter} with suppressions file {@code config/suppressions.xml}:
301 * </p>
302 * <pre>
303 * &lt;module name=&quot;SuppressionXpathFilter&quot;&gt;
304 *   &lt;property name=&quot;file&quot; value=&quot;config/suppressions.xml&quot;/&gt;
305 *   &lt;property name=&quot;optional&quot; value=&quot;false&quot;/&gt;
306 * &lt;/module&gt;
307 * </pre>
308 * <p>
309 * A <a href="https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd"><em>
310 * suppressions XML document</em></a>
311 * contains a set of {@code suppress} and {@code suppress-xpath} elements,
312 * where each {@code suppress-xpath} element can have the following attributes:
313 * </p>
314 * <ul>
315 * <li>
316 * {@code files} - a <a href="https://checkstyle.org/property_types.html#regexp">Regular Expression</a>
317 * matched against the file name associated with an audit event. It is optional.
318 * </li>
319 * <li>
320 * {@code checks} - a <a href="https://checkstyle.org/property_types.html#regexp">Regular Expression</a>
321 * matched against the name of the check associated with an audit event.
322 * Optional as long as {@code id} or {@code message} is specified.
323 * </li>
324 * <li>
325 * {@code message} - a <a href="https://checkstyle.org/property_types.html#regexp">Regular Expression</a>
326 * matched against the message of the check associated with an audit event.
327 * Optional as long as {@code checks} or {@code id} is specified.
328 * </li>
329 * <li>
330 * {@code id} - a <a href="https://checkstyle.org/property_types.html#string">string</a> matched against
331 * the ID of the check associated with an audit event.
332 * Optional as long as {@code checks} or {@code message} is specified.
333 * </li>
334 * <li>
335 * {@code query} - a <a href="https://checkstyle.org/property_types.html#string">string</a> xpath query. It is optional.
336 * </li>
337 * </ul>
338 * <p>
339 * Each audit event is checked against each {@code suppress} and {@code suppress-xpath} element.
340 * It is suppressed if all specified attributes match against the audit event.
341 * </p>
342 * <p>
343 * ATTENTION: filtering by message is dependant on runtime locale.
344 * If project is running in different languages it is better to avoid filtering by message.
345 * </p>
346 * <p>
347 * The following suppressions XML document directs a {@code SuppressionXpathFilter} to reject
348 * {@code CyclomaticComplexity} violations for all methods with name <i>sayHelloWorld</i> inside
349 * <i>FileOne</i> and <i>FileTwo</i> files:
350 * </p>
351 * <p>
352 * Currently, xpath queries support one type of attribute {@code @text}. {@code @text} -
353 * addresses to the text value of the node. For example: variable name, annotation name,
354 * text content and etc. Only the following token types support {@code @text} attribute:
355 * {@code TokenTypes.IDENT}, {@code TokenTypes.STRING_LITERAL}, {@code TokenTypes.CHAR_LITERAL},
356 * {@code TokenTypes.NUM_LONG}, {@code TokenTypes.NUM_INT}, {@code TokenTypes.NUM_DOUBLE},
357 * {@code TokenTypes.NUM_FLOAT}.
358 * These token types were selected because only their text values are different
359 * in content from token type and represent text value from file and can be used
360 * in xpath queries for more accurate results. Other token types always have constant values.
361 * </p>
362 * <pre>
363 * &lt;?xml version=&quot;1.0&quot;?&gt;
364 *
365 * &lt;!DOCTYPE suppressions PUBLIC
366 * &quot;-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2//EN&quot;
367 * &quot;https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd&quot;&gt;
368 *
369 * &lt;suppressions&gt;
370 *   &lt;suppress-xpath checks=&quot;CyclomaticComplexity&quot;
371 *   files=&quot;FileOne.java,FileTwo.java&quot;
372 *   query=&quot;//METHOD_DEF[./IDENT[@text='sayHelloWorld']]&quot;/&gt;
373 * &lt;/suppressions&gt;
374 * </pre>
375 * <p>
376 * Suppress checks for package definitions:
377 * </p>
378 * <pre>
379 * &lt;suppress-xpath checks=".*" query="/PACKAGE_DEF"/&gt;
380 * </pre>
381 * <p>
382 * Suppress checks for parent element of the first variable definition:
383 * </p>
384 * <pre>
385 * &lt;suppress-xpath checks=".*" query="(//VARIABLE_DEF)[1]/.."/&gt;
386 * </pre>
387 * <p>
388 * Suppress checks for elements which are either class definitions, either method definitions.
389 * </p>
390 * <pre>
391 * &lt;suppress-xpath checks=".*" query="//CLASS_DEF | //METHOD_DEF"/&gt;
392 * </pre>
393 * <p>
394 * Suppress checks for certain methods:
395 * </p>
396 * <pre>
397 * &lt;suppress-xpath checks=&quot;.*&quot; query=&quot;//METHOD_DEF[./IDENT[@text='getSomeVar'
398 *           or @text='setSomeVar']]&quot;/&gt;
399 * </pre>
400 * <p>
401 * Suppress checks for variable <i>testVariable</i> inside <i>testMethod</i>
402 * method inside <i>TestClass</i> class.
403 * </p>
404 * <pre>
405 * &lt;suppress-xpath checks=&quot;.*&quot; query=&quot;/CLASS_DEF[@text='TestClass']
406 *           //METHOD_DEF[./IDENT[@text='testMethod']]
407 *           //VARIABLE_DEF[./IDENT[@text='testVariable']]&quot;/&gt;
408 * </pre>
409 * <p>
410 * In the following sample, violations for {@code LeftCurly} check will be suppressed
411 * for classes with name <i>Main</i> or for methods with name <i>calculate</i>.
412 * </p>
413 * <pre>
414 * &lt;suppress-xpath checks=&quot;LeftCurly&quot; query=&quot;/CLASS_DEF[./IDENT[@text='Main']]//*
415 *           | //METHOD_DEF[./IDENT[@text='calculate']]/*&quot;/&gt;
416 * </pre>
417 * <p>
418 * The following example demonstrates how to suppress {@code RequireThis} violations
419 * for variable <i>age</i> inside <i>changeAge</i> method.
420 * </p>
421 * <pre>
422 * &lt;suppress-xpath checks=&quot;RequireThis&quot;
423 *      query=&quot;/CLASS_DEF[./IDENT[@text='InputTest']]
424 *           //METHOD_DEF[./IDENT[@text='changeAge']]//ASSIGN/IDENT[@text='age']&quot;/&gt;
425 * </pre>
426 * <pre>
427 * public class InputTest {
428 *   private int age = 23;
429 *
430 *   public void changeAge() {
431 *     age = 24; //violation will be suppressed
432 *   }
433 * }
434 * </pre>
435 * <p>
436 * Suppress {@code IllegalThrows} violations only for methods with name <i>throwsMethod</i>
437 * and only for {@code RuntimeException} exceptions. Double colon is used for axis iterations.
438 * In the following example {@code ancestor} axis is used to iterate all ancestor nodes
439 * of the current node with type {@code METHOD_DEF} and name <i>throwsMethod</i>.
440 * Please read more about xpath axes at <a href="https://www.w3schools.com/xml/xpath_axes.asp">
441 * W3Schools Xpath Axes</a>.
442 * </p>
443 * <pre>
444 * &lt;suppress-xpath checks="IllegalThrows" query="//LITERAL_THROWS
445 *           /IDENT[@text='RuntimeException' and
446 *           ./ancestor::METHOD_DEF[./IDENT[@text='throwsMethod']]]"/&gt;
447 * </pre>
448 * <pre>
449 * public class InputTest {
450 *   public void throwsMethod() throws RuntimeException { // violation will be suppressed
451 *   }
452 *
453 *   public void sampleMethod() throws RuntimeException { // will throw violation here
454 *   }
455 * }
456 * </pre>
457 * <p>
458 * The following sample demonstrates how to suppress all violations for method
459 * itself and all descendants. {@code descendant-or-self} axis iterates through
460 * current node and all children nodes at any level. Keyword {@code node()}
461 * selects node elements. Please read more about xpath syntax at
462 * <a href="https://www.w3schools.com/xml/xpath_syntax.asp">W3Schools Xpath Syntax</a>.
463 * </p>
464 * <pre>
465 * &lt;suppress-xpath checks=".*" query="//METHOD_DEF[./IDENT[@text='legacyMethod']]
466 *           /descendant-or-self::node()"/&gt;
467 * </pre>
468 * <p>
469 * Some elements can be suppressed in different ways. For example, to suppress
470 * violation on variable {@code wordCount} in following code:
471 * </p>
472 * <pre>
473 * public class InputTest {
474 *     private int wordCount = 11;
475 * }
476 * </pre>
477 * <p>
478 * You need to look at AST of such code by our CLI tool:
479 * </p>
480 * <pre>
481 * $ java -jar checkstyle-X.XX-all.jar -t InputTest.java
482 * CLASS_DEF -&gt; CLASS_DEF [1:0]
483 * |--MODIFIERS -&gt; MODIFIERS [1:0]
484 * |   `--LITERAL_PUBLIC -&gt; public [1:0]
485 * |--LITERAL_CLASS -&gt; class [1:7]
486 * |--IDENT -&gt; InputTest [1:13]
487 * `--OBJBLOCK -&gt; OBJBLOCK [1:23]
488 * |--LCURLY -&gt; { [1:23]
489 * |--VARIABLE_DEF -&gt; VARIABLE_DEF [2:4]
490 * |   |--MODIFIERS -&gt; MODIFIERS [2:4]
491 * |   |   `--LITERAL_PRIVATE -&gt; private [2:4]
492 * |   |--TYPE -&gt; TYPE [2:12]
493 * |   |   `--LITERAL_INT -&gt; int [2:12]
494 * |   |--IDENT -&gt; wordCount [2:16]
495 * |   |--ASSIGN -&gt; = [2:26]
496 * |   |   `--EXPR -&gt; EXPR [2:28]
497 * |   |       `--NUM_INT -&gt; 11 [2:28]
498 * |   `--SEMI -&gt; ; [2:30]
499 * `--RCURLY -&gt; } [3:0]
500 * </pre>
501 * <p>
502 * The easiest way is to suppress by variable name. As you can see {@code VARIABLE_DEF}
503 * node refers to variable declaration statement and has child node with token type
504 * {@code IDENT} which is used for storing class, method, variable names.
505 * </p>
506 * <p>
507 * The following example demonstrates how variable can be queried by its name:
508 * </p>
509 * <pre>
510 * &lt;suppress-xpath checks="." query="//VARIABLE_DEF[
511 *             ./IDENT[@text='wordCount']]"/&gt;
512 * </pre>
513 * <p>
514 * Another way is to suppress by variable value. Again, if you look at the printed
515 * AST tree above, you will notice that one of the grandchildren of {@code VARIABLE_DEF}
516 * node is responsible for storing variable value -{@code NUM_INT} with value <b>11</b>.
517 * </p>
518 * <p>
519 * The following example demonstrates how variable can be queried by its value,
520 * same approach applies to {@code String, char, float, double, int, long} data types:
521 * </p>
522 * <pre>
523 * &lt;suppress-xpath checks="." query="//VARIABLE_DEF[.//NUM_INT[@text=11]]"/&gt;
524 * </pre>
525 * <p>
526 * Next example is about suppressing method with certain annotation by its name and element value.
527 * </p>
528 * <pre>
529 * public class InputTest {
530 *             &#64;Generated("first") // should not be suppressed
531 *             public void test1() {
532 *             }
533 *
534 *             &#64;Generated("second") // should be suppressed
535 *             public void test2() {
536 *             }
537 *         }
538 * </pre>
539 * <p>
540 * First of all we need to look at AST tree printed by our CLI tool:
541 * </p>
542 * <pre>
543 * $ java -jar checkstyle-X.XX-all.jar -t InputTest.java
544 * CLASS_DEF -&gt; CLASS_DEF [1:0]
545 * |--MODIFIERS -&gt; MODIFIERS [1:0]
546 * |   `--LITERAL_PUBLIC -&gt; public [1:0]
547 * |--LITERAL_CLASS -&gt; class [1:7]
548 * |--IDENT -&gt; InputTest [1:13]
549 * `--OBJBLOCK -&gt; OBJBLOCK [1:23]
550 * |--LCURLY -&gt; { [1:23]
551 * |--METHOD_DEF -&gt; METHOD_DEF [2:4]
552 * |   |--MODIFIERS -&gt; MODIFIERS [2:4]
553 * |   |   |--ANNOTATION -&gt; ANNOTATION [2:4]
554 * |   |   |   |--AT -&gt; @ [2:4]
555 * |   |   |   |--IDENT -&gt; Generated [2:5]
556 * |   |   |   |--LPAREN -&gt; ( [2:14]
557 * |   |   |   |--EXPR -&gt; EXPR [2:15]
558 * |   |   |   |   `--STRING_LITERAL -&gt; "first" [2:15]
559 * |   |   |   `--RPAREN -&gt; ) [2:22]
560 * |   |   `--LITERAL_PUBLIC -&gt; public [3:4]
561 * |   |--TYPE -&gt; TYPE [3:11]
562 * |   |   `--LITERAL_VOID -&gt; void [3:11]
563 * |   |--IDENT -&gt; test1 [3:16]
564 * |   |--LPAREN -&gt; ( [3:21]
565 * |   |--PARAMETERS -&gt; PARAMETERS [3:22]
566 * |   |--RPAREN -&gt; ) [3:22]
567 * |   `--SLIST -&gt; { [3:24]
568 * |       `--RCURLY -&gt; } [4:4]
569 * |--METHOD_DEF -&gt; METHOD_DEF [6:4]
570 * |   |--MODIFIERS -&gt; MODIFIERS [6:4]
571 * |   |   |--ANNOTATION -&gt; ANNOTATION [6:4]
572 * |   |   |   |--AT -&gt; @ [6:4]
573 * |   |   |   |--IDENT -&gt; Generated [6:5]
574 * |   |   |   |--LPAREN -&gt; ( [6:14]
575 * |   |   |   |--EXPR -&gt; EXPR [6:15]
576 * |   |   |   |   `--STRING_LITERAL -&gt; "second" [6:15]
577 * |   |   |   `--RPAREN -&gt; ) [6:23]
578 * |   |   `--LITERAL_PUBLIC -&gt; public [7:4]
579 * |   |--TYPE -&gt; TYPE [7:11]
580 * |   |   `--LITERAL_VOID -&gt; void [7:11]
581 * |   |--IDENT -&gt; test2 [7:16]
582 * |   |--LPAREN -&gt; ( [7:21]
583 * |   |--PARAMETERS -&gt; PARAMETERS [7:22]
584 * |   |--RPAREN -&gt; ) [7:22]
585 * |   `--SLIST -&gt; { [7:24]
586 * |       `--RCURLY -&gt; } [8:4]
587 * `--RCURLY -&gt; } [9:0]
588 * </pre>
589 * <p>
590 * AST node {@code ANNOTATION -> ANNOTATION [6:4]} has direct child
591 * {@code IDENT -> Generated [6:5]}, therefore can be queried by {@code IDENT} value:
592 * </p>
593 * <pre>
594 * &lt;suppress-xpath checks="." query="//METHOD_DEF[
595 *             .//ANNOTATION/IDENT[@text='Generated']]"/&gt;
596 * </pre>
597 * <p>
598 * The problem with query above that it will suppress violations for all methods
599 * with annotation {@code @Generated}. In order to suppress methods with
600 * {@code @Generated("second")} annotations only, you need to look at AST tree again.
601 * Value of the {@code ANNOTATION} node is stored inside sub-node with token type
602 * {@code STRING_LITERAL}. Use the following query to suppress methods with
603 * {@code @Generated("second")} annotation:
604 * </p>
605 * <pre>
606 * &lt;suppress-xpath checks="." query="//METHOD_DEF[.//ANNOTATION[
607 *             ./IDENT[@text='Generated'] and ./EXPR/STRING_LITERAL[@text='second']]]"/&gt;
608 * </pre>
609 *
610 * @since 8.6
611 * @noinspection NonFinalFieldReferenceInEquals, NonFinalFieldReferencedInHashCode
612 */
613public class SuppressionXpathFilter extends AutomaticBean implements
614        TreeWalkerFilter, ExternalResourceHolder {
615
616    /** Specify the location of the <em>suppressions XML document</em> file. */
617    private String file;
618    /**
619     * Control what to do when the file is not existing.
620     * If optional is set to false the file must exist, or else it ends with error.
621     * On the other hand if optional is true and file is not found,
622     * the filter accepts all audit events.
623     */
624    private boolean optional;
625    /** Set of individual xpath suppresses. */
626    private Set<TreeWalkerFilter> filters = new HashSet<>();
627
628    /**
629     * Setter to specify the location of the <em>suppressions XML document</em> file.
630     * @param fileName name of the suppressions file.
631     */
632    public void setFile(String fileName) {
633        file = fileName;
634    }
635
636    /**
637     * Setter to control what to do when the file is not existing.
638     * If optional is set to false the file must exist, or else it ends with error.
639     * On the other hand if optional is true and file is not found,
640     * the filter accepts all audit events.
641     * @param optional tells if config file existence is optional.
642     */
643    public void setOptional(boolean optional) {
644        this.optional = optional;
645    }
646
647    @Override
648    public boolean equals(Object obj) {
649        if (this == obj) {
650            return true;
651        }
652        if (obj == null || getClass() != obj.getClass()) {
653            return false;
654        }
655        final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
656        return Objects.equals(filters, suppressionXpathFilter.filters);
657    }
658
659    @Override
660    public int hashCode() {
661        return Objects.hash(filters);
662    }
663
664    @Override
665    public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
666        boolean result = true;
667        for (TreeWalkerFilter filter : filters) {
668            if (!filter.accept(treeWalkerAuditEvent)) {
669                result = false;
670                break;
671            }
672        }
673        return result;
674    }
675
676    @Override
677    public Set<String> getExternalResourceLocations() {
678        return Collections.singleton(file);
679    }
680
681    @Override
682    protected void finishLocalSetup() throws CheckstyleException {
683        if (file != null) {
684            if (optional) {
685                if (FilterUtil.isFileExists(file)) {
686                    filters = SuppressionsLoader.loadXpathSuppressions(file);
687                }
688                else {
689                    filters = new HashSet<>();
690                }
691            }
692            else {
693                filters = SuppressionsLoader.loadXpathSuppressions(file);
694            }
695        }
696    }
697
698}