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 * <module name="SuppressionXpathSingleFilter"> 087 * <property name="files" value="File(One|Two)\.java"/> 088 * <property name="checks" value="MethodName"/> 089 * <property name="query" value="(//CLASS_DEF[@text='FileOne']/OBJBLOCK/ 090 * METHOD_DEF[@text='MyMethod']/IDENT)| 091 * (//CLASS_DEF[@text='FileTwo']/OBJBLOCK/METHOD_DEF[@text='MyMethod']/IDENT)"/> 092 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 116 * <property name="checks" value="MethodName"/> 117 * <property name="message" value="MyMethod[0-9]"/> 118 * </module> 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 * <module name="MethodName"> 136 * <property name="id" value="MethodName1"/> 137 * <property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/> 138 * <module/> 139 * <module name="SuppressionXpathSingleFilter"> 140 * <property name="files" value="FileOne.java"/> 141 * <property name="id" value="MethodName1"/> 142 * <module/> 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 * <module name="SuppressionXpathSingleFilter"> 161 * <property name="checks" value="PackageName"/> 162 * <property name="query" value="/PACKAGE_DEF[@text='File']/IDENT"/> 163 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 178 * <property name="checks" value="RedundantModifier"/> 179 * <property name="query" value="/INTERFACE_DEF//*"/> 180 * <module/> 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 * <module name="SuppressionXpathSingleFilter"> 195 * <property name="files" value="FileOne.java"/> 196 * <property name="checks" value="MyMethod"/> 197 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 217 * <property name="checks" value=".*"/> 218 * <property name="query" 219 * value="(//CLASS_DEF[@text='FileOne'])| 220 * (//CLASS_DEF[@text='FileOne']/OBJBLOCK/METHOD_DEF[@text='MyMethod']/IDENT)"/> 221 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 242 * <property name="checks" value="MethodName"/> 243 * <property name="query" value="//CLASS_DEF[@text='FileOne']/OBJBLOCK/ 244 * METHOD_DEF[@text='MyMethod1' or @text='MyMethod2']/IDENT"/> 245 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 263 * <property name="checks" value="LocalFinalVariableName"/> 264 * <property name="query" value="//CLASS_DEF[@text='TestClass']/OBJBLOCK 265 * /METHOD_DEF[@text='testMethod']/SLIST 266 * /VARIABLE_DEF[@text='testVariable1']/IDENT"/> 267 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 287 * <property name="checks" value="LeftCurly"/> 288 * <property name="query" value="//CLASS_DEF[@text='TestClass']/OBJBLOCK 289 * /METHOD_DEF[@text='testMethod1']/SLIST*"/> 290 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 312 * <property name="checks" value="RequireThis"/> 313 * <property name="query" value="//CLASS_DEF[@text='InputTest'] 314 * //METHOD_DEF[@text='changeAge']//ASSIGN[@text='age']/IDENT"/> 315 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 340 * <property name="checks" value="IllegalThrows"/> 341 * <property name="query" value="//LITERAL_THROWS/IDENT[ 342 * ..[@text='RuntimeException'] and ./ancestor::METHOD_DEF[@text='throwsMethod']]"/> 343 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 366 * <property name="checks" value=".*"/> 367 * <property name="query" value="//METHOD_DEF[@text='TestMethod1'] 368 * /descendant-or-self::node()"/> 369 * </module> 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 * <module name="SuppressionXpathSingleFilter"> 394 * <property name="files" value="[\\/]src[\\/]test[\\/]java[\\/]"/> 395 * <property name="checks" value="Javadoc*"/> 396 * </module> 397 * <module name="SuppressionXpathSingleFilter"> 398 * <property name="files" value=".*Tests\.java"> 399 * <property name="checks" value="Javadoc*"> 400 * </module> 401 * <module name="SuppressionXpathSingleFilter"> 402 * <property name="files" value="generated-sources"> 403 * <property name="checks" value="[a-zA-Z0-9]*"> 404 * </module> 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}