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