View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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#SuppressionFilter">
38   * SuppressionFilter</a>.
39   * Additionally, filter processes {@code suppress-xpath} elements,
40   * which contains xpath-expressions. Xpath-expressions are queries for
41   * suppressed nodes inside the AST tree.
42   * </div>
43   *
44   * <p>
45   * Currently, filter does not support the following checks:
46   * </p>
47   * <ul id="IncompatibleChecks">
48   * <li>
49   * NoCodeInFile (reason is that AST is not generated for a file not containing code)
50   * </li>
51   * <li>
52   * Regexp (reason is at
53   * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
54   * </li>
55   * <li>
56   * RegexpSinglelineJava (reason is at
57   * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
58   * </li>
59   * </ul>
60   *
61   * <p>
62   * Also, the filter does not support suppressions inside javadoc reported by Javadoc checks:
63   * </p>
64   * <ul id="JavadocChecks">
65   * <li>
66   * AtclauseOrder
67   * </li>
68   * <li>
69   * JavadocBlockTagLocation
70   * </li>
71   * <li>
72   * JavadocMethod
73   * </li>
74   * <li>
75   * JavadocMissingLeadingAsterisk
76   * </li>
77   * <li>
78   * JavadocMissingWhitespaceAfterAsterisk
79   * </li>
80   * <li>
81   * JavadocParagraph
82   * </li>
83   * <li>
84   * JavadocStyle
85   * </li>
86   * <li>
87   * JavadocTagContinuationIndentation
88   * </li>
89   * <li>
90   * JavadocType
91   * </li>
92   * <li>
93   * MissingDeprecated
94   * </li>
95   * <li>
96   * NonEmptyAtclauseDescription
97   * </li>
98   * <li>
99   * RequireEmptyLineBeforeBlockTagGroup
100  * </li>
101  * <li>
102  * SingleLineJavadoc
103  * </li>
104  * <li>
105  * SummaryJavadoc
106  * </li>
107  * <li>
108  * WriteTag
109  * </li>
110  * </ul>
111  *
112  * <p>
113  * Note, that support for these Checks will be available after resolving issue
114  * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a>.
115  * </p>
116  *
117  * <p>
118  * Currently, filter supports the following xpath axes:
119  * </p>
120  * <ul>
121  * <li>
122  * ancestor
123  * </li>
124  * <li>
125  * ancestor-or-self
126  * </li>
127  * <li>
128  * attribute
129  * </li>
130  * <li>
131  * child
132  * </li>
133  * <li>
134  * descendant
135  * </li>
136  * <li>
137  * descendant-or-self
138  * </li>
139  * <li>
140  * following
141  * </li>
142  * <li>
143  * following-sibling
144  * </li>
145  * <li>
146  * parent
147  * </li>
148  * <li>
149  * preceding
150  * </li>
151  * <li>
152  * preceding-sibling
153  * </li>
154  * <li>
155  * self
156  * </li>
157  * </ul>
158  *
159  * <p>
160  * You can use the command line helper tool to generate xpath suppressions based on your
161  * configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a>
162  * for more details.
163  * </p>
164  *
165  * <p>
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  * <ul>
185  * <li>
186  * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
187  * Type is {@code java.lang.String}.
188  * Default value is {@code null}.
189  * </li>
190  * <li>
191  * Property {@code optional} - Control what to do when the file is not existing.
192  * If optional is set to false the file must exist, or else it ends with error.
193  * On the other hand if optional is true and file is not found, the filter accepts all audit events.
194  * Type is {@code boolean}.
195  * Default value is {@code false}.
196  * </li>
197  * </ul>
198  *
199  * <p>
200  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
201  * </p>
202  *
203  * @since 8.6
204  */
205 public class SuppressionXpathFilter extends AbstractAutomaticBean implements
206         TreeWalkerFilter, ExternalResourceHolder {
207 
208     /** Set of individual xpath suppresses. */
209     private final Set<TreeWalkerFilter> filters = new HashSet<>();
210 
211     /** Specify the location of the <em>suppressions XML document</em> file. */
212     private String file;
213     /**
214      * Control what to do when the file is not existing.
215      * If optional is set to false the file must exist, or else it ends with error.
216      * On the other hand if optional is true and file is not found,
217      * the filter accepts all audit events.
218      */
219     private boolean optional;
220 
221     /**
222      * Setter to specify the location of the <em>suppressions XML document</em> file.
223      *
224      * @param fileName name of the suppressions file.
225      * @since 8.6
226      */
227     public void setFile(String fileName) {
228         file = fileName;
229     }
230 
231     /**
232      * Setter to control what to do when the file is not existing.
233      * If optional is set to false the file must exist, or else it ends with error.
234      * On the other hand if optional is true and file is not found,
235      * the filter accepts all audit events.
236      *
237      * @param optional tells if config file existence is optional.
238      * @since 8.6
239      */
240     public void setOptional(boolean optional) {
241         this.optional = optional;
242     }
243 
244     @Override
245     public boolean equals(Object obj) {
246         if (this == obj) {
247             return true;
248         }
249         if (obj == null || getClass() != obj.getClass()) {
250             return false;
251         }
252         final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
253         return Objects.equals(filters, suppressionXpathFilter.filters);
254     }
255 
256     @Override
257     public int hashCode() {
258         return Objects.hash(filters);
259     }
260 
261     @Override
262     public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
263         boolean result = true;
264         for (TreeWalkerFilter filter : filters) {
265             if (!filter.accept(treeWalkerAuditEvent)) {
266                 result = false;
267                 break;
268             }
269         }
270         return result;
271     }
272 
273     @Override
274     public Set<String> getExternalResourceLocations() {
275         return Collections.singleton(file);
276     }
277 
278     @Override
279     protected void finishLocalSetup() throws CheckstyleException {
280         if (file != null) {
281             if (optional) {
282                 if (FilterUtil.isFileExists(file)) {
283                     filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
284                 }
285             }
286             else {
287                 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
288             }
289         }
290     }
291 
292 }