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 184 * of {@code suppress} and {@code suppress-xpath} elements, where 185 * each {@code suppress-xpath} element can have the 186 * following attributes: 187 * </p> 188 * <ul> 189 * <li> 190 * {@code files} - 191 * a <a href="../property_types.html#Pattern">Pattern</a> 192 * matched against the file name associated with an audit 193 * event. It is optional. If unmatched, all Unix path separators (/) 194 * are converted to Windows separators (\) and retried. 195 * </li> 196 * <li> 197 * {@code checks} - 198 * a <a href="../property_types.html#Pattern">Pattern</a> 199 * matched against the name of the check associated with an audit 200 * event. Optional as long as {@code id} or {@code message} is specified. 201 * </li> 202 * <li> 203 * {@code message} - 204 * a <a href="../property_types.html#Pattern">Pattern</a> 205 * matched against the message of the check associated with an audit 206 * event. Optional as long as {@code checks} or {@code id} is specified. 207 * </li> 208 * <li> 209 * {@code id} - 210 * a <a href="../property_types.html#String">String</a> 211 * matched against the ID of the check associated with an audit 212 * event. Optional as long as {@code checks} or {@code message} is specified. 213 * </li> 214 * <li> 215 * {@code query} - 216 * a <a href="../property_types.html#String">String</a> 217 * xpath query. It is optional. 218 * </li> 219 * </ul> 220 * 221 * <p> 222 * Each audit event is checked against 223 * each {@code suppress} and {@code suppress-xpath} element. It is 224 * suppressed if all specified attributes match against the audit 225 * event. 226 * </p> 227 * 228 * <p> 229 * ATTENTION: filtering by message is dependent on runtime locale. If project is running 230 * in different languages it is better to avoid filtering by message. 231 * </p> 232 * 233 * @since 8.6 234 */ 235public class SuppressionXpathFilter extends AbstractAutomaticBean implements 236 TreeWalkerFilter, ExternalResourceHolder { 237 238 /** Set of individual xpath suppresses. */ 239 private final Set<TreeWalkerFilter> filters = new HashSet<>(); 240 241 /** Specify the location of the <em>suppressions XML document</em> file. */ 242 private String file; 243 /** 244 * Control what to do when the file is not existing. 245 * If optional is set to false the file must exist, or else it ends with error. 246 * On the other hand if optional is true and file is not found, 247 * the filter accepts all audit events. 248 */ 249 private boolean optional; 250 251 /** 252 * Creates a new {@code SuppressionXpathFilter} instance. 253 */ 254 public SuppressionXpathFilter() { 255 // no code by default 256 } 257 258 /** 259 * Setter to specify the location of the <em>suppressions XML document</em> file. 260 * 261 * @param fileName name of the suppressions file. 262 * @since 8.6 263 */ 264 public void setFile(String fileName) { 265 file = fileName; 266 } 267 268 /** 269 * Setter to control what to do when the file is not existing. 270 * If optional is set to false the file must exist, or else it ends with error. 271 * On the other hand if optional is true and file is not found, 272 * the filter accepts all audit events. 273 * 274 * @param optional tells if config file existence is optional. 275 * @since 8.6 276 */ 277 public void setOptional(boolean optional) { 278 this.optional = optional; 279 } 280 281 @Override 282 public boolean equals(Object obj) { 283 if (this == obj) { 284 return true; 285 } 286 if (obj == null || getClass() != obj.getClass()) { 287 return false; 288 } 289 final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj; 290 return Objects.equals(filters, suppressionXpathFilter.filters); 291 } 292 293 @Override 294 public int hashCode() { 295 return Objects.hash(filters); 296 } 297 298 @Override 299 public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) { 300 boolean result = true; 301 for (TreeWalkerFilter filter : filters) { 302 if (!filter.accept(treeWalkerAuditEvent)) { 303 result = false; 304 break; 305 } 306 } 307 return result; 308 } 309 310 @Override 311 public Set<String> getExternalResourceLocations() { 312 return Set.of(file); 313 } 314 315 @Override 316 protected void finishLocalSetup() throws CheckstyleException { 317 if (file != null) { 318 if (optional) { 319 if (FilterUtil.isFileExists(file)) { 320 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file)); 321 } 322 } 323 else { 324 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file)); 325 } 326 } 327 } 328 329}