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