View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.AbstractAutomaticBean;
28  import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
29  import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
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   * <div>
36   * Filter {@code SuppressionXpathFilter} works as
37   * <a href="https://checkstyle.org/filters/suppressionfilter.html">
38   * SuppressionFilter</a>, but also processes {@code suppress-xpath} elements,
39   * which contain xpath-expressions. Xpath-expressions are queries for
40   * suppressed nodes inside the AST tree.
41   * </div>
42   *
43   * <p>
44   * Currently, filter does not support the following checks:
45   * </p>
46   * <ul id="IncompatibleChecks">
47   * <li>
48   * NoCodeInFile (reason is that AST is not generated for a file not containing code)
49   * </li>
50   * <li>
51   * Regexp (reason is at
52   * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
53   * </li>
54   * <li>
55   * RegexpSinglelineJava (reason is at
56   * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
57   * </li>
58   * </ul>
59   *
60   * <p>
61   * Also, the filter does not support suppressions inside javadoc reported by Javadoc checks:
62   * </p>
63   * <ul id="JavadocChecks">
64   * <li>
65   * AtclauseOrder
66   * </li>
67   * <li>
68   * JavadocBlockTagLocation
69   * </li>
70   * <li>
71   * JavadocMethod
72   * </li>
73   * <li>
74   * JavadocMissingLeadingAsterisk
75   * </li>
76   * <li>
77   * JavadocMissingWhitespaceAfterAsterisk
78   * </li>
79   * <li>
80   * JavadocParagraph
81   * </li>
82   * <li>
83   * JavadocStyle
84   * </li>
85   * <li>
86   * JavadocTagContinuationIndentation
87   * </li>
88   * <li>
89   * JavadocType
90   * </li>
91   * <li>
92   * MissingDeprecated
93   * </li>
94   * <li>
95   * NonEmptyAtclauseDescription
96   * </li>
97   * <li>
98   * RequireEmptyLineBeforeBlockTagGroup
99   * </li>
100  * <li>
101  * SingleLineJavadoc
102  * </li>
103  * <li>
104  * SummaryJavadoc
105  * </li>
106  * <li>
107  * WriteTag
108  * </li>
109  * </ul>
110  *
111  * <p>
112  * Note, that support for these Checks will be available after resolving issue
113  * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a>.
114  * </p>
115  *
116  * <p>
117  * Currently, filter supports the following xpath axes:
118  * </p>
119  * <ul>
120  * <li>
121  * ancestor
122  * </li>
123  * <li>
124  * ancestor-or-self
125  * </li>
126  * <li>
127  * attribute
128  * </li>
129  * <li>
130  * child
131  * </li>
132  * <li>
133  * descendant
134  * </li>
135  * <li>
136  * descendant-or-self
137  * </li>
138  * <li>
139  * following
140  * </li>
141  * <li>
142  * following-sibling
143  * </li>
144  * <li>
145  * parent
146  * </li>
147  * <li>
148  * preceding
149  * </li>
150  * <li>
151  * preceding-sibling
152  * </li>
153  * <li>
154  * self
155  * </li>
156  * </ul>
157  *
158  * <p>
159  * You can use the command line helper tool to generate xpath suppressions based on your
160  * configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a>
161  * for more details.
162  * </p>
163  *
164  * <p>
165  * Notes:
166  * The suppression file location is checked in following order:
167  * </p>
168  * <ol>
169  * <li>
170  * as a filesystem location
171  * </li>
172  * <li>
173  * if no file found, and the location starts with either {@code http://} or {@code https://},
174  * then it is interpreted as a URL
175  * </li>
176  * <li>
177  * if no file found, then passed to the {@code ClassLoader.getResource()} method.
178  * </li>
179  * </ol>
180  *
181  * <p>
182  * SuppressionXpathFilter can suppress Checks that have Treewalker as parent module.
183  * </p>
184  *
185  * <p>
186  * A <a href="/dtds/suppressions_1_2_xpath_experimental.dtd"><em>suppressions XML
187  * document</em></a> contains a set
188  * of {@code suppress} and {@code suppress-xpath} elements, where
189  * each {@code suppress-xpath} element can have the
190  * following attributes:
191  * </p>
192  * <ul>
193  * <li>
194  * {@code files} -
195  * a <a href="../property_types.html#Pattern">Pattern</a>
196  * matched against the file name associated with an audit
197  * event. It is optional.
198  * </li>
199  * <li>
200  * {@code checks} -
201  * a <a href="../property_types.html#Pattern">Pattern</a>
202  * matched against the name of the check associated with an audit
203  * event. Optional as long as {@code id} or {@code message} is specified.
204  * </li>
205  * <li>
206  * {@code message} -
207  * a <a href="../property_types.html#Pattern">Pattern</a>
208  * matched against the message of the check associated with an audit
209  * event. Optional as long as {@code checks} or {@code id} is specified.
210  * </li>
211  * <li>
212  * {@code id} -
213  * a <a href="../property_types.html#String">String</a>
214  * matched against the ID of the check associated with an audit
215  * event. Optional as long as {@code checks} or {@code message} is specified.
216  * </li>
217  * <li>
218  * {@code query} -
219  * a <a href="../property_types.html#String">String</a>
220  * xpath query. It is optional.
221  * </li>
222  * </ul>
223  *
224  * <p>
225  * Each audit event is checked against
226  * each {@code suppress} and {@code suppress-xpath} element. It is
227  * suppressed if all specified attributes match against the audit
228  * event.
229  * </p>
230  *
231  * <p>
232  * ATTENTION: filtering by message is dependent on runtime locale. If project is running
233  * in different languages it is better to avoid filtering by message.
234  * </p>
235  *
236  * <ul>
237  * <li>
238  * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
239  * Type is {@code java.lang.String}.
240  * Default value is {@code null}.
241  * </li>
242  * <li>
243  * Property {@code optional} - Control what to do when the file is not existing.
244  * If optional is set to false the file must exist, or else it ends with error.
245  * On the other hand if optional is true and file is not found, the filter accepts all audit events.
246  * Type is {@code boolean}.
247  * Default value is {@code false}.
248  * </li>
249  * </ul>
250  *
251  * <p>
252  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
253  * </p>
254  *
255  * @since 8.6
256  */
257 public class SuppressionXpathFilter extends AbstractAutomaticBean implements
258         TreeWalkerFilter, ExternalResourceHolder {
259 
260     /** Set of individual xpath suppresses. */
261     private final Set<TreeWalkerFilter> filters = new HashSet<>();
262 
263     /** Specify the location of the <em>suppressions XML document</em> file. */
264     private String file;
265     /**
266      * Control what to do when the file is not existing.
267      * If optional is set to false the file must exist, or else it ends with error.
268      * On the other hand if optional is true and file is not found,
269      * the filter accepts all audit events.
270      */
271     private boolean optional;
272 
273     /**
274      * Setter to specify the location of the <em>suppressions XML document</em> file.
275      *
276      * @param fileName name of the suppressions file.
277      * @since 8.6
278      */
279     public void setFile(String fileName) {
280         file = fileName;
281     }
282 
283     /**
284      * Setter to control what to do when the file is not existing.
285      * If optional is set to false the file must exist, or else it ends with error.
286      * On the other hand if optional is true and file is not found,
287      * the filter accepts all audit events.
288      *
289      * @param optional tells if config file existence is optional.
290      * @since 8.6
291      */
292     public void setOptional(boolean optional) {
293         this.optional = optional;
294     }
295 
296     @Override
297     public boolean equals(Object obj) {
298         if (this == obj) {
299             return true;
300         }
301         if (obj == null || getClass() != obj.getClass()) {
302             return false;
303         }
304         final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
305         return Objects.equals(filters, suppressionXpathFilter.filters);
306     }
307 
308     @Override
309     public int hashCode() {
310         return Objects.hash(filters);
311     }
312 
313     @Override
314     public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
315         boolean result = true;
316         for (TreeWalkerFilter filter : filters) {
317             if (!filter.accept(treeWalkerAuditEvent)) {
318                 result = false;
319                 break;
320             }
321         }
322         return result;
323     }
324 
325     @Override
326     public Set<String> getExternalResourceLocations() {
327         return Collections.singleton(file);
328     }
329 
330     @Override
331     protected void finishLocalSetup() throws CheckstyleException {
332         if (file != null) {
333             if (optional) {
334                 if (FilterUtil.isFileExists(file)) {
335                     filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
336                 }
337             }
338             else {
339                 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
340             }
341         }
342     }
343 
344 }