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   * <p>
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   * </p>
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   * <p>
60   * Also, the filter does not support suppressions inside javadoc reported by Javadoc checks:
61   * </p>
62   * <ul id="JavadocChecks">
63   * <li>
64   * AtclauseOrder
65   * </li>
66   * <li>
67   * JavadocBlockTagLocation
68   * </li>
69   * <li>
70   * JavadocMethod
71   * </li>
72   * <li>
73   * JavadocMissingLeadingAsterisk
74   * </li>
75   * <li>
76   * JavadocMissingWhitespaceAfterAsterisk
77   * </li>
78   * <li>
79   * JavadocParagraph
80   * </li>
81   * <li>
82   * JavadocStyle
83   * </li>
84   * <li>
85   * JavadocTagContinuationIndentation
86   * </li>
87   * <li>
88   * JavadocType
89   * </li>
90   * <li>
91   * MissingDeprecated
92   * </li>
93   * <li>
94   * NonEmptyAtclauseDescription
95   * </li>
96   * <li>
97   * RequireEmptyLineBeforeBlockTagGroup
98   * </li>
99   * <li>
100  * SingleLineJavadoc
101  * </li>
102  * <li>
103  * SummaryJavadoc
104  * </li>
105  * <li>
106  * WriteTag
107  * </li>
108  * </ul>
109  * <p>
110  * Note, that support for these Checks will be available after resolving issue
111  * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</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  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
193  * </p>
194  *
195  * @since 8.6
196  */
197 public class SuppressionXpathFilter extends AbstractAutomaticBean implements
198         TreeWalkerFilter, ExternalResourceHolder {
199 
200     /** Set of individual xpath suppresses. */
201     private final Set<TreeWalkerFilter> filters = new HashSet<>();
202 
203     /** Specify the location of the <em>suppressions XML document</em> file. */
204     private String file;
205     /**
206      * Control what to do when the file is not existing.
207      * If optional is set to false the file must exist, or else it ends with error.
208      * On the other hand if optional is true and file is not found,
209      * the filter accepts all audit events.
210      */
211     private boolean optional;
212 
213     /**
214      * Setter to specify the location of the <em>suppressions XML document</em> file.
215      *
216      * @param fileName name of the suppressions file.
217      * @since 8.6
218      */
219     public void setFile(String fileName) {
220         file = fileName;
221     }
222 
223     /**
224      * Setter to control what to do when the file is not existing.
225      * If optional is set to false the file must exist, or else it ends with error.
226      * On the other hand if optional is true and file is not found,
227      * the filter accepts all audit events.
228      *
229      * @param optional tells if config file existence is optional.
230      * @since 8.6
231      */
232     public void setOptional(boolean optional) {
233         this.optional = optional;
234     }
235 
236     @Override
237     public boolean equals(Object obj) {
238         if (this == obj) {
239             return true;
240         }
241         if (obj == null || getClass() != obj.getClass()) {
242             return false;
243         }
244         final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
245         return Objects.equals(filters, suppressionXpathFilter.filters);
246     }
247 
248     @Override
249     public int hashCode() {
250         return Objects.hash(filters);
251     }
252 
253     @Override
254     public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
255         boolean result = true;
256         for (TreeWalkerFilter filter : filters) {
257             if (!filter.accept(treeWalkerAuditEvent)) {
258                 result = false;
259                 break;
260             }
261         }
262         return result;
263     }
264 
265     @Override
266     public Set<String> getExternalResourceLocations() {
267         return Collections.singleton(file);
268     }
269 
270     @Override
271     protected void finishLocalSetup() throws CheckstyleException {
272         if (file != null) {
273             if (optional) {
274                 if (FilterUtil.isFileExists(file)) {
275                     filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
276                 }
277             }
278             else {
279                 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
280             }
281         }
282     }
283 
284 }