001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.HashSet; 023import java.util.Objects; 024import java.util.Set; 025 026import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean; 027import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent; 028import com.puppycrawl.tools.checkstyle.TreeWalkerFilter; 029import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 030import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 031import com.puppycrawl.tools.checkstyle.utils.FilterUtil; 032 033/** 034 * <div> 035 * Filter {@code SuppressionXpathFilter} works as 036 * <a href="https://checkstyle.org/filters/suppressionfilter.html"> 037 * SuppressionFilter</a>, but also processes {@code suppress-xpath} elements, 038 * which contain xpath-expressions. Xpath-expressions are queries for 039 * suppressed nodes inside the AST tree. 040 * </div> 041 * 042 * <p> 043 * Currently, filter does not support the following checks: 044 * </p> 045 * <ul id="IncompatibleChecks"> 046 * <li> 047 * NoCodeInFile (reason is that AST is not generated for a file not containing code) 048 * </li> 049 * <li> 050 * Regexp (reason is at 051 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>) 052 * </li> 053 * <li> 054 * RegexpSinglelineJava (reason is at 055 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>) 056 * </li> 057 * </ul> 058 * 059 * <p> 060 * Also, the filter does not support suppressions inside javadoc reported by Javadoc checks: 061 * </p> 062 * <ul id="JavadocChecks"> 063 * <li> 064 * AtclauseOrder 065 * </li> 066 * <li> 067 * JavadocBlockTagLocation 068 * </li> 069 * <li> 070 * JavadocMethod 071 * </li> 072 * <li> 073 * JavadocMissingLeadingAsterisk 074 * </li> 075 * <li> 076 * JavadocMissingWhitespaceAfterAsterisk 077 * </li> 078 * <li> 079 * JavadocParagraph 080 * </li> 081 * <li> 082 * JavadocTagContinuationIndentation 083 * </li> 084 * <li> 085 * JavadocType 086 * </li> 087 * <li> 088 * MissingDeprecated 089 * </li> 090 * <li> 091 * NonEmptyAtclauseDescription 092 * </li> 093 * <li> 094 * RequireEmptyLineBeforeBlockTagGroup 095 * </li> 096 * <li> 097 * SingleLineJavadoc 098 * </li> 099 * <li> 100 * SummaryJavadoc 101 * </li> 102 * <li> 103 * WriteTag 104 * </li> 105 * </ul> 106 * 107 * <p> 108 * Note, that support for these Checks will be available after resolving issue 109 * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a>. 110 * </p> 111 * 112 * <p> 113 * Currently, filter supports the following xpath axes: 114 * </p> 115 * <ul> 116 * <li> 117 * ancestor 118 * </li> 119 * <li> 120 * ancestor-or-self 121 * </li> 122 * <li> 123 * attribute 124 * </li> 125 * <li> 126 * child 127 * </li> 128 * <li> 129 * descendant 130 * </li> 131 * <li> 132 * descendant-or-self 133 * </li> 134 * <li> 135 * following 136 * </li> 137 * <li> 138 * following-sibling 139 * </li> 140 * <li> 141 * parent 142 * </li> 143 * <li> 144 * preceding 145 * </li> 146 * <li> 147 * preceding-sibling 148 * </li> 149 * <li> 150 * self 151 * </li> 152 * </ul> 153 * 154 * <p> 155 * You can use the command line helper tool to generate xpath suppressions based on your 156 * configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a> 157 * for more details. 158 * </p> 159 * 160 * <p> 161 * Notes: 162 * The suppression file location is checked in following order: 163 * </p> 164 * <ol> 165 * <li> 166 * as a filesystem location 167 * </li> 168 * <li> 169 * if no file found, and the location starts with either {@code http://} or {@code https://}, 170 * then it is interpreted as a URL 171 * </li> 172 * <li> 173 * if no file found, then passed to the {@code ClassLoader.getResource()} method. 174 * </li> 175 * </ol> 176 * 177 * <p> 178 * SuppressionXpathFilter can suppress Checks that have Treewalker as parent module. 179 * </p> 180 * 181 * <p> 182 * A <a href="/dtds/suppressions_1_2_xpath.dtd"><em>suppressions XML 183 * document</em></a> contains a set of {@code suppress} and {@code suppress-xpath} elements, where 184 * each {@code suppress-xpath} element can have the following attributes: 185 * </p> 186 * <ul> 187 * <li> 188 * {@code files} - a <a href="https://checkstyle.org/property-types.html#Pattern">Pattern</a> 189 * matched against the file name associated with an audit 190 * event. It is optional. If unmatched, all Unix path separators (/) 191 * are converted to Windows separators (\) and retried. 192 * </li> 193 * <li> 194 * {@code checks} - a <a href="https://checkstyle.org/property-types.html#Pattern">Pattern</a> 195 * matched against the name of the check associated with an audit 196 * event. Optional as long as {@code id} or {@code message} is specified. 197 * </li> 198 * <li> 199 * {@code message} - a <a href="https://checkstyle.org/property-types.html#Pattern">Pattern</a> 200 * matched against the message of the check associated with an audit 201 * event. Optional as long as {@code checks} or {@code id} is specified. 202 * </li> 203 * <li> 204 * {@code id} - a <a href="https://checkstyle.org/property-types.html#String">String</a> 205 * matched against the ID of the check associated with an audit 206 * event. Optional as long as {@code checks} or {@code message} is specified. 207 * </li> 208 * <li> 209 * {@code query} - a <a href="https://checkstyle.org/property-types.html#String">String</a> 210 * xpath query. It is optional. 211 * </li> 212 * </ul> 213 * 214 * <p> 215 * Each audit event is checked against 216 * each {@code suppress} and {@code suppress-xpath} element. It is 217 * suppressed if all specified attributes match against the audit event. 218 * </p> 219 * 220 * <p> 221 * ATTENTION: filtering by message is dependent on runtime locale. If project is running 222 * in different languages it is better to avoid filtering by message. 223 * </p> 224 * 225 * @since 8.6 226 */ 227public class SuppressionXpathFilter extends AbstractAutomaticBean implements 228 TreeWalkerFilter, ExternalResourceHolder { 229 230 /** Set of individual xpath suppresses. */ 231 private final Set<TreeWalkerFilter> filters = new HashSet<>(); 232 233 /** Specify the location of the <em>suppressions XML document</em> file. */ 234 private String file; 235 /** 236 * Control what to do when the file is not existing. 237 * If optional is set to false the file must exist, or else it ends with error. 238 * On the other hand if optional is true and file is not found, 239 * the filter accepts all audit events. 240 */ 241 private boolean optional; 242 243 /** 244 * Creates a new {@code SuppressionXpathFilter} instance. 245 */ 246 public SuppressionXpathFilter() { 247 // no code by default 248 } 249 250 /** 251 * Setter to specify the location of the <em>suppressions XML document</em> file. 252 * 253 * @param fileName name of the suppressions file. 254 * @since 8.6 255 */ 256 public void setFile(String fileName) { 257 file = fileName; 258 } 259 260 /** 261 * Setter to control what to do when the file is not existing. 262 * If optional is set to false the file must exist, or else it ends with error. 263 * On the other hand if optional is true and file is not found, 264 * the filter accepts all audit events. 265 * 266 * @param optional tells if config file existence is optional. 267 * @since 8.6 268 */ 269 public void setOptional(boolean optional) { 270 this.optional = optional; 271 } 272 273 @Override 274 public boolean equals(Object obj) { 275 if (this == obj) { 276 return true; 277 } 278 if (obj == null || getClass() != obj.getClass()) { 279 return false; 280 } 281 final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj; 282 return Objects.equals(filters, suppressionXpathFilter.filters); 283 } 284 285 @Override 286 public int hashCode() { 287 return Objects.hash(filters); 288 } 289 290 @Override 291 public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) { 292 boolean result = true; 293 for (TreeWalkerFilter filter : filters) { 294 if (!filter.accept(treeWalkerAuditEvent)) { 295 result = false; 296 break; 297 } 298 } 299 return result; 300 } 301 302 @Override 303 public Set<String> getExternalResourceLocations() { 304 return Set.of(file); 305 } 306 307 @Override 308 protected void finishLocalSetup() throws CheckstyleException { 309 if (file != null) { 310 if (optional) { 311 if (FilterUtil.isFileExists(file)) { 312 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file)); 313 } 314 } 315 else { 316 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file)); 317 } 318 } 319 } 320 321}