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  * @since 8.6
237  */
238 public class SuppressionXpathFilter extends AbstractAutomaticBean implements
239         TreeWalkerFilter, ExternalResourceHolder {
240 
241     /** Set of individual xpath suppresses. */
242     private final Set<TreeWalkerFilter> filters = new HashSet<>();
243 
244     /** Specify the location of the <em>suppressions XML document</em> file. */
245     private String file;
246     /**
247      * Control what to do when the file is not existing.
248      * If optional is set to false the file must exist, or else it ends with error.
249      * On the other hand if optional is true and file is not found,
250      * the filter accepts all audit events.
251      */
252     private boolean optional;
253 
254     /**
255      * Setter to specify the location of the <em>suppressions XML document</em> file.
256      *
257      * @param fileName name of the suppressions file.
258      * @since 8.6
259      */
260     public void setFile(String fileName) {
261         file = fileName;
262     }
263 
264     /**
265      * Setter to control what to do when the file is not existing.
266      * If optional is set to false the file must exist, or else it ends with error.
267      * On the other hand if optional is true and file is not found,
268      * the filter accepts all audit events.
269      *
270      * @param optional tells if config file existence is optional.
271      * @since 8.6
272      */
273     public void setOptional(boolean optional) {
274         this.optional = optional;
275     }
276 
277     @Override
278     public boolean equals(Object obj) {
279         if (this == obj) {
280             return true;
281         }
282         if (obj == null || getClass() != obj.getClass()) {
283             return false;
284         }
285         final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
286         return Objects.equals(filters, suppressionXpathFilter.filters);
287     }
288 
289     @Override
290     public int hashCode() {
291         return Objects.hash(filters);
292     }
293 
294     @Override
295     public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
296         boolean result = true;
297         for (TreeWalkerFilter filter : filters) {
298             if (!filter.accept(treeWalkerAuditEvent)) {
299                 result = false;
300                 break;
301             }
302         }
303         return result;
304     }
305 
306     @Override
307     public Set<String> getExternalResourceLocations() {
308         return Collections.singleton(file);
309     }
310 
311     @Override
312     protected void finishLocalSetup() throws CheckstyleException {
313         if (file != null) {
314             if (optional) {
315                 if (FilterUtil.isFileExists(file)) {
316                     filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
317                 }
318             }
319             else {
320                 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
321             }
322         }
323     }
324 
325 }