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