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