001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2022 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.filters;
021
022import java.util.regex.Pattern;
023
024import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
025import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
026import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
027
028/**
029 * <p>
030 * Filter {@code SuppressionXpathSingleFilter} suppresses audit events for Checks
031 * violations in the specified file, class, checks, message, module id, and xpath.
032 * </p>
033 * <p>
034 * Rationale: To allow users use suppressions configured in the same config with
035 * other modules. SuppressionFilter and SuppressionXpathFilter are require separate file.
036 * </p>
037 * <p>
038 * Advice: If checkstyle configuration is used for several projects, single suppressions
039 * on common files/folders is better to put in checkstyle configuration as common rule.
040 * All suppression that are for specific file names is better to keep in project
041 * specific config file.
042 * </p>
043 * <p>
044 * Attention: This filter only supports single suppression, and will need multiple
045 * instances if users wants to suppress multiple violations.
046 * </p>
047 * <p>
048 * SuppressionXpathSingleFilter can suppress Checks that have Treewalker as parent module.
049 * </p>
050 * <ul>
051 * <li>
052 * Property {@code files} - Define a Regular Expression matched against the file
053 * name associated with an audit event.
054 * Type is {@code java.util.regex.Pattern}.
055 * Default value is {@code null}.
056 * </li>
057 * <li>
058 * Property {@code checks} - Define a Regular Expression matched against the name
059 * of the check associated with an audit event.
060 * Type is {@code java.util.regex.Pattern}.
061 * Default value is {@code null}.
062 * </li>
063 * <li>
064 * Property {@code message} - Define a Regular Expression matched against the message
065 * of the check associated with an audit event.
066 * Type is {@code java.util.regex.Pattern}.
067 * Default value is {@code null}.
068 * </li>
069 * <li>
070 * Property {@code id} - Define a string matched against the ID of the check
071 * associated with an audit event.
072 * Type is {@code java.lang.String}.
073 * Default value is {@code null}.
074 * </li>
075 * <li>
076 * Property {@code query} - Define a string xpath query.
077 * Type is {@code java.lang.String}.
078 * Default value is {@code null}.
079 * </li>
080 * </ul>
081 * <p>
082 * To configure to suppress the MethodName check for all methods with
083 * name MyMethod inside FileOne and FileTwo files:
084 * </p>
085 * <pre>
086 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
087 *   &lt;property name=&quot;files&quot; value=&quot;File(One|Two)\.java&quot;/&gt;
088 *   &lt;property name=&quot;checks&quot; value=&quot;MethodName&quot;/&gt;
089 *   &lt;property name=&quot;query&quot; value=&quot;(//CLASS_DEF[@text='FileOne']/OBJBLOCK/
090 *             METHOD_DEF[@text='MyMethod']/IDENT)|
091 *             (//CLASS_DEF[@text='FileTwo']/OBJBLOCK/METHOD_DEF[@text='MyMethod']/IDENT)&quot;/&gt;
092 * &lt;/module&gt;
093 * </pre>
094 * <p>
095 * Code example:
096 * </p>
097 * <pre>
098 * public class FileOne {
099 *   public void MyMethod() {} // OK
100 * }
101 *
102 * public class FileTwo {
103 *   public void MyMethod() {} // OK
104 * }
105 *
106 * public class FileThree {
107 *   public void MyMethod() {} // violation, name 'MyMethod'
108 *                             // must match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
109 * }
110 * </pre>
111 * <p>
112 * To suppress MethodName check for method names matched pattern 'MyMethod[0-9]':
113 * </p>
114 * <pre>
115 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
116 *   &lt;property name=&quot;checks&quot; value=&quot;MethodName&quot;/&gt;
117 *   &lt;property name=&quot;message&quot; value=&quot;MyMethod[0-9]&quot;/&gt;
118 * &lt;/module&gt;
119 * </pre>
120 * <p>
121 * Code Example:
122 * </p>
123 * <pre>
124 * public class FileOne {
125 *   public void MyMethod1() {} // OK
126 *   public void MyMethod2() {} // OK
127 *   public void MyMethodA() {} // violation, name 'MyMethodA' must
128 *                              // match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
129 * }
130 * </pre>
131 * <p>
132 * To suppress checks being specified by id property:
133 * </p>
134 * <pre>
135 * &lt;module name=&quot;MethodName&quot;&gt;
136 *   &lt;property name=&quot;id&quot; value=&quot;MethodName1&quot;/&gt;
137 *   &lt;property name=&quot;format&quot; value=&quot;^[a-z](_?[a-zA-Z0-9]+)*$&quot;/&gt;
138 * &lt;module/&gt;
139 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
140 *   &lt;property name=&quot;files&quot; value=&quot;FileOne.java&quot;/&gt;
141 *   &lt;property name=&quot;id&quot; value=&quot;MethodName1&quot;/&gt;
142 * &lt;module/&gt;
143 * </pre>
144 * <p>
145 * Code example:
146 * </p>
147 * <pre>
148 * public class FileOne {
149 *   public void MyMethod() {} // OK
150 * }
151 * public class FileTwo {
152 *   public void MyMethod() {} // violation,  name 'MyMethod' must
153 *                             //match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
154 * }
155 * </pre>
156 * <p>
157 * To suppress checks for all package definitions:
158 * </p>
159 * <pre>
160 * &lt;module name=&quot;SuppressionXpathSingleFilter"&gt;
161 *   &lt;property name=&quot;checks&quot; value=&quot;PackageName&quot;/&gt;
162 *   &lt;property name=&quot;query&quot; value=&quot;/PACKAGE_DEF[@text='File']/IDENT&quot;/&gt;
163 * &lt;/module&gt;
164 * </pre>
165 * <p>
166 * Code example:
167 * </p>
168 * <pre>
169 * package File; // OK
170 *
171 * public class FileOne {}
172 * </pre>
173 * <p>
174 * To suppress RedundantModifier check for interface definitions:
175 * </p>
176 * <pre>
177 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
178 *   &lt;property name=&quot;checks&quot; value=&quot;RedundantModifier&quot;/&gt;
179 *   &lt;property name=&quot;query&quot; value=&quot;/INTERFACE_DEF//*&quot;/&gt;
180 * &lt;module/&gt;
181 * </pre>
182 * <p>
183 * Code Example:
184 * </p>
185 * <pre>
186 * public interface TestClass {
187 *   public static final int CONSTANT1 = 1;  // OK
188 * }
189 * </pre>
190 * <p>
191 * To suppress checks in the FileOne file by non-query:
192 * </p>
193 * <pre>
194 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
195 *   &lt;property name=&quot;files&quot; value=&quot;FileOne.java&quot;/&gt;
196 *   &lt;property name=&quot;checks&quot; value=&quot;MyMethod&quot;/&gt;
197 * &lt;/module&gt;
198 * </pre>
199 * <p>
200 * Code example:
201 * </p>
202 * <pre>
203 * public class FileOne {
204 *   public void MyMethod() {} // OK
205 * }
206 *
207 * public class FileTwo {
208 *   public void MyMethod() {} // violation, name 'MyMethod'
209 *                             // must match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
210 * }
211 * </pre>
212 * <p>
213 * Suppress checks for elements which are either class definitions, either method definitions:
214 * </p>
215 * <pre>
216 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
217 *   &lt;property name=&quot;checks&quot; value=&quot;.*&quot;/&gt;
218 *   &lt;property name=&quot;query&quot;
219 *             value=&quot;(//CLASS_DEF[@text='FileOne'])|
220 *             (//CLASS_DEF[@text='FileOne']/OBJBLOCK/METHOD_DEF[@text='MyMethod']/IDENT)&quot;/&gt;
221 * &lt;/module&gt;
222 * </pre>
223 * <p>
224 * Code example:
225 * </p>
226 * <pre>
227 * abstract class FileOne { // OK
228 *   public void MyMethod() {} // OK
229 * }
230 *
231 * abstract class FileTwo { // violation of the AbstractClassName check,
232 *                          // it should match the pattern "^Abstract.+$"
233 *   public void MyMethod() {} // violation, name 'MyMethod'
234 *                             // must match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
235 * }
236 * </pre>
237 * <p>
238 * Suppress checks for MyMethod1 or MyMethod2 methods:
239 * </p>
240 * <pre>
241 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
242 *   &lt;property name=&quot;checks&quot; value=&quot;MethodName&quot;/&gt;
243 *   &lt;property name=&quot;query&quot; value=&quot;//CLASS_DEF[@text='FileOne']/OBJBLOCK/
244 *             METHOD_DEF[@text='MyMethod1' or @text='MyMethod2']/IDENT&quot;/&gt;
245 * &lt;/module&gt;
246 * </pre>
247 * <p>
248 * Code example:
249 * </p>
250 * <pre>
251 * public class FileOne {
252 *   public void MyMethod1() {} // OK
253 *   public void MyMethod2() {} // OK
254 *   public void MyMethod3() {} // violation, name 'MyMethod3' must
255 *                              // match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
256 * }
257 * </pre>
258 * <p>
259 * Suppress checks for variable testVariable inside testMethod method inside TestClass class:
260 * </p>
261 * <pre>
262 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
263 *   &lt;property name=&quot;checks&quot; value=&quot;LocalFinalVariableName&quot;/&gt;
264 *   &lt;property name=&quot;query&quot; value=&quot;//CLASS_DEF[@text='TestClass']/OBJBLOCK
265 *         /METHOD_DEF[@text='testMethod']/SLIST
266 *         /VARIABLE_DEF[@text='testVariable1']/IDENT&quot;/&gt;
267 * &lt;/module&gt;
268 * </pre>
269 * <p>
270 * Code Example:
271 * </p>
272 * <pre>
273 * public class TestClass {
274 *   public void testMethod() {
275 *     final int testVariable1 = 10; // OK
276 *     final int testVariable2 = 10; // violation of the LocalFinalVariableName check,
277 *                                   // name 'testVariable2' must match pattern '^[A-Z][A-Z0-9]*$'
278 *   }
279 * }
280 * </pre>
281 * <p>
282 * In the following sample, violations for LeftCurly check will be suppressed
283 * for classes with name Main or for methods with name calculate.
284 * </p>
285 * <pre>
286 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
287 *   &lt;property name=&quot;checks&quot; value=&quot;LeftCurly&quot;/&gt;
288 *   &lt;property name=&quot;query&quot; value=&quot;//CLASS_DEF[@text='TestClass']/OBJBLOCK
289 *         /METHOD_DEF[@text='testMethod1']/SLIST*&quot;/&gt;
290 * &lt;/module&gt;
291 * </pre>
292 * <p>
293 * Code Example:
294 * </p>
295 * <pre>
296 * public class TestClass {
297 *   public void testMethod1()
298 *   { // OK
299 *   }
300 *
301 *   public void testMethod2()
302 *   { // violation, '{' should be on the previous line
303 *   }
304 * }
305 * </pre>
306 * <p>
307 * The following example demonstrates how to suppress RequireThis violations for
308 * variable age inside changeAge method.
309 * </p>
310 * <pre>
311 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
312 *   &lt;property name=&quot;checks&quot; value=&quot;RequireThis&quot;/&gt;
313 *   &lt;property name=&quot;query&quot; value=&quot;//CLASS_DEF[@text='InputTest']
314 *         //METHOD_DEF[@text='changeAge']//ASSIGN[@text='age']/IDENT&quot;/&gt;
315 * &lt;/module&gt;
316 * </pre>
317 * <p>
318 * Code Example:
319 * </p>
320 * <pre>
321 * public class InputTest {
322 *   private int age = 23;
323 *
324 *   public void changeAge() {
325 *     age = 24; // violation will be suppressed
326 *   }
327 * }
328 * </pre>
329 * <p>
330 * Suppress {@code IllegalThrows} violations only for methods with name
331 * <i>throwsMethod</i> and only for {@code RuntimeException} exceptions.
332 * Double colon is used for axis iterations. In the following example
333 * {@code ancestor} axis is used to iterate all ancestor nodes of the current
334 * node with type {@code METHOD_DEF} and name <i>throwsMethod</i>.
335 * Please read more about xpath axes at
336 * <a href="https://www.w3schools.com/xml/xpath_axes.asp">W3Schools Xpath Axes</a>.
337 * </p>
338 * <pre>
339 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
340 *   &lt;property name=&quot;checks&quot; value=&quot;IllegalThrows&quot;/&gt;
341 *   &lt;property name=&quot;query&quot; value=&quot;//LITERAL_THROWS/IDENT[
342 *       ..[@text='RuntimeException'] and ./ancestor::METHOD_DEF[@text='throwsMethod']]&quot;/&gt;
343 * &lt;/module&gt;
344 * </pre>
345 * <p>
346 * Code Example:
347 * </p>
348 * <pre>
349 * public class InputTest {
350 *   public void throwsMethod() throws RuntimeException { // violation will be suppressed
351 *   }
352 *
353 *   public void sampleMethod() throws RuntimeException { // will throw violation here
354 *   }
355 * }
356 * </pre>
357 * <p>
358 * The following sample demonstrates how to suppress all violations for method
359 * itself and all descendants. {@code descendant-or-self} axis iterates through
360 * current node and all children nodes at any level. Keyword {@code node()}
361 * selects node elements. Please read more about xpath syntax at
362 * <a href="https://www.w3schools.com/xml/xpath_syntax.asp">W3Schools Xpath Syntax</a>.
363 * </p>
364 * <pre>
365 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
366 *   &lt;property name=&quot;checks&quot; value=&quot;.*&quot;/&gt;
367 *   &lt;property name=&quot;query&quot; value=&quot;//METHOD_DEF[@text='TestMethod1']
368 *         /descendant-or-self::node()&quot;/&gt;
369 * &lt;/module&gt;
370 * </pre>
371 * <p>
372 * Code Example:
373 * </p>
374 * <pre>
375 * public class TestClass {
376 *   public void TestMethod1() { // OK
377 *     final int num = 10; // OK
378 *   }
379 *
380 *   public void TestMethod2() { // violation of the MethodName check,
381 *                               // name 'TestMethod2' must match pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
382 *     final int num = 10; // violation of the LocalFinalVariableName check,
383 *                         // name 'num' must match pattern '^[A-Z][A-Z0-9]*$'
384 *   }
385 * }
386 * </pre>
387 * <p>
388 * The following example is an example of what checks would be suppressed while
389 * building Spring projects with checkstyle plugin. Please find more information at:
390 * <a href="https://github.com/spring-io/spring-javaformat">spring-javaformat</a>
391 * </p>
392 * <pre>
393 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
394 *   &lt;property name=&quot;files&quot; value=&quot;[\\/]src[\\/]test[\\/]java[\\/]&quot;/&gt;
395 *   &lt;property name=&quot;checks&quot; value=&quot;Javadoc*&quot;/&gt;
396 * &lt;/module&gt;
397 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
398 *   &lt;property name=&quot;files&quot; value=&quot;.*Tests\.java&quot;&gt;
399 *   &lt;property name=&quot;checks&quot; value=&quot;Javadoc*&quot;&gt;
400 * &lt;/module&gt;
401 * &lt;module name=&quot;SuppressionXpathSingleFilter&quot;&gt;
402 *   &lt;property name=&quot;files&quot; value=&quot;generated-sources&quot;&gt;
403 *   &lt;property name=&quot;checks&quot; value=&quot;[a-zA-Z0-9]*&quot;&gt;
404 * &lt;/module&gt;
405 * </pre>
406 * <p>
407 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
408 * </p>
409 *
410 * @since 8.18
411 */
412public class SuppressionXpathSingleFilter extends AutomaticBean implements
413        TreeWalkerFilter {
414    /**
415     * XpathFilterElement instance.
416     */
417    private XpathFilterElement xpathFilter;
418    /**
419     * Define a Regular Expression matched against the file name associated with an audit event.
420     */
421    private Pattern files;
422    /**
423     * Define a Regular Expression matched against the name of the check associated
424     * with an audit event.
425     */
426    private Pattern checks;
427    /**
428     * Define a Regular Expression matched against the message of the check
429     * associated with an audit event.
430     */
431    private Pattern message;
432    /**
433     * Define a string matched against the ID of the check associated with an audit event.
434     */
435    private String id;
436    /**
437     * Define a string xpath query.
438     */
439    private String query;
440
441    /**
442     * Setter to define a Regular Expression matched against the file name
443     * associated with an audit event.
444     *
445     * @param files the name of the file
446     */
447    public void setFiles(String files) {
448        if (files == null) {
449            this.files = null;
450        }
451        else {
452            this.files = Pattern.compile(files);
453        }
454    }
455
456    /**
457     * Setter to define a Regular Expression matched against the name of the check
458     * associated with an audit event.
459     *
460     * @param checks the name of the check
461     */
462    public void setChecks(String checks) {
463        if (checks == null) {
464            this.checks = null;
465        }
466        else {
467            this.checks = Pattern.compile(checks);
468        }
469    }
470
471    /**
472     * Setter to define a Regular Expression matched against the message of
473     * the check associated with an audit event.
474     *
475     * @param message the message of the check
476     */
477    public void setMessage(String message) {
478        if (message == null) {
479            this.message = null;
480        }
481        else {
482            this.message = Pattern.compile(message);
483        }
484    }
485
486    /**
487     * Setter to define a string matched against the ID of the check associated
488     * with an audit event.
489     *
490     * @param id the ID of the check
491     */
492    public void setId(String id) {
493        this.id = id;
494    }
495
496    /**
497     * Setter to define a string xpath query.
498     *
499     * @param query the xpath query
500     */
501    public void setQuery(String query) {
502        this.query = query;
503    }
504
505    @Override
506    protected void finishLocalSetup() {
507        xpathFilter = new XpathFilterElement(files, checks, message, id, query);
508    }
509
510    @Override
511    public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
512        return xpathFilter.accept(treeWalkerAuditEvent);
513    }
514
515}