001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.Collections; 023import java.util.HashSet; 024import java.util.Objects; 025import java.util.Set; 026 027import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean; 028import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent; 029import com.puppycrawl.tools.checkstyle.TreeWalkerFilter; 030import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 031import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 032import com.puppycrawl.tools.checkstyle.utils.FilterUtil; 033 034/** 035 * <div> 036 * Filter {@code SuppressionXpathFilter} works as 037 * <a href="https://checkstyle.org/filters/suppressionfilter.html#SuppressionFilter"> 038 * SuppressionFilter</a>. 039 * Additionally, filter processes {@code suppress-xpath} elements, 040 * which contains xpath-expressions. Xpath-expressions are queries for 041 * suppressed nodes inside the AST tree. 042 * </div> 043 * 044 * <p> 045 * Currently, filter does not support the following checks: 046 * </p> 047 * <ul id="IncompatibleChecks"> 048 * <li> 049 * NoCodeInFile (reason is that AST is not generated for a file not containing code) 050 * </li> 051 * <li> 052 * Regexp (reason is at 053 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>) 054 * </li> 055 * <li> 056 * RegexpSinglelineJava (reason is at 057 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>) 058 * </li> 059 * </ul> 060 * 061 * <p> 062 * Also, the filter does not support suppressions inside javadoc reported by Javadoc checks: 063 * </p> 064 * <ul id="JavadocChecks"> 065 * <li> 066 * AtclauseOrder 067 * </li> 068 * <li> 069 * JavadocBlockTagLocation 070 * </li> 071 * <li> 072 * JavadocMethod 073 * </li> 074 * <li> 075 * JavadocMissingLeadingAsterisk 076 * </li> 077 * <li> 078 * JavadocMissingWhitespaceAfterAsterisk 079 * </li> 080 * <li> 081 * JavadocParagraph 082 * </li> 083 * <li> 084 * JavadocStyle 085 * </li> 086 * <li> 087 * JavadocTagContinuationIndentation 088 * </li> 089 * <li> 090 * JavadocType 091 * </li> 092 * <li> 093 * MissingDeprecated 094 * </li> 095 * <li> 096 * NonEmptyAtclauseDescription 097 * </li> 098 * <li> 099 * RequireEmptyLineBeforeBlockTagGroup 100 * </li> 101 * <li> 102 * SingleLineJavadoc 103 * </li> 104 * <li> 105 * SummaryJavadoc 106 * </li> 107 * <li> 108 * WriteTag 109 * </li> 110 * </ul> 111 * 112 * <p> 113 * Note, that support for these Checks will be available after resolving issue 114 * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a>. 115 * </p> 116 * 117 * <p> 118 * Currently, filter supports the following xpath axes: 119 * </p> 120 * <ul> 121 * <li> 122 * ancestor 123 * </li> 124 * <li> 125 * ancestor-or-self 126 * </li> 127 * <li> 128 * attribute 129 * </li> 130 * <li> 131 * child 132 * </li> 133 * <li> 134 * descendant 135 * </li> 136 * <li> 137 * descendant-or-self 138 * </li> 139 * <li> 140 * following 141 * </li> 142 * <li> 143 * following-sibling 144 * </li> 145 * <li> 146 * parent 147 * </li> 148 * <li> 149 * preceding 150 * </li> 151 * <li> 152 * preceding-sibling 153 * </li> 154 * <li> 155 * self 156 * </li> 157 * </ul> 158 * 159 * <p> 160 * You can use the command line helper tool to generate xpath suppressions based on your 161 * configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a> 162 * for more details. 163 * </p> 164 * 165 * <p> 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 * <ul> 185 * <li> 186 * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file. 187 * Type is {@code java.lang.String}. 188 * Default value is {@code null}. 189 * </li> 190 * <li> 191 * Property {@code optional} - Control what to do when the file is not existing. 192 * If optional is set to false the file must exist, or else it ends with error. 193 * On the other hand if optional is true and file is not found, the filter accepts all audit events. 194 * Type is {@code boolean}. 195 * Default value is {@code false}. 196 * </li> 197 * </ul> 198 * 199 * <p> 200 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 201 * </p> 202 * 203 * @since 8.6 204 */ 205public class SuppressionXpathFilter extends AbstractAutomaticBean implements 206 TreeWalkerFilter, ExternalResourceHolder { 207 208 /** Set of individual xpath suppresses. */ 209 private final Set<TreeWalkerFilter> filters = new HashSet<>(); 210 211 /** Specify the location of the <em>suppressions XML document</em> file. */ 212 private String file; 213 /** 214 * Control what to do when the file is not existing. 215 * If optional is set to false the file must exist, or else it ends with error. 216 * On the other hand if optional is true and file is not found, 217 * the filter accepts all audit events. 218 */ 219 private boolean optional; 220 221 /** 222 * Setter to specify the location of the <em>suppressions XML document</em> file. 223 * 224 * @param fileName name of the suppressions file. 225 * @since 8.6 226 */ 227 public void setFile(String fileName) { 228 file = fileName; 229 } 230 231 /** 232 * Setter to control what to do when the file is not existing. 233 * If optional is set to false the file must exist, or else it ends with error. 234 * On the other hand if optional is true and file is not found, 235 * the filter accepts all audit events. 236 * 237 * @param optional tells if config file existence is optional. 238 * @since 8.6 239 */ 240 public void setOptional(boolean optional) { 241 this.optional = optional; 242 } 243 244 @Override 245 public boolean equals(Object obj) { 246 if (this == obj) { 247 return true; 248 } 249 if (obj == null || getClass() != obj.getClass()) { 250 return false; 251 } 252 final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj; 253 return Objects.equals(filters, suppressionXpathFilter.filters); 254 } 255 256 @Override 257 public int hashCode() { 258 return Objects.hash(filters); 259 } 260 261 @Override 262 public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) { 263 boolean result = true; 264 for (TreeWalkerFilter filter : filters) { 265 if (!filter.accept(treeWalkerAuditEvent)) { 266 result = false; 267 break; 268 } 269 } 270 return result; 271 } 272 273 @Override 274 public Set<String> getExternalResourceLocations() { 275 return Collections.singleton(file); 276 } 277 278 @Override 279 protected void finishLocalSetup() throws CheckstyleException { 280 if (file != null) { 281 if (optional) { 282 if (FilterUtil.isFileExists(file)) { 283 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file)); 284 } 285 } 286 else { 287 filters.addAll(SuppressionsLoader.loadXpathSuppressions(file)); 288 } 289 } 290 } 291 292}