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