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