1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 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#SuppressionFilter"> 38 * SuppressionFilter</a>. 39 * Additionally, filter processes {@code suppress-xpath} elements, 40 * which contains xpath-expressions. Xpath-expressions are queries for 41 * suppressed nodes inside the AST tree. 42 * </div> 43 * 44 * <p> 45 * Currently, filter does not support the following checks: 46 * </p> 47 * <ul id="IncompatibleChecks"> 48 * <li> 49 * NoCodeInFile (reason is that AST is not generated for a file not containing code) 50 * </li> 51 * <li> 52 * Regexp (reason is at 53 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>) 54 * </li> 55 * <li> 56 * RegexpSinglelineJava (reason is at 57 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>) 58 * </li> 59 * </ul> 60 * 61 * <p> 62 * Also, the filter does not support suppressions inside javadoc reported by Javadoc checks: 63 * </p> 64 * <ul id="JavadocChecks"> 65 * <li> 66 * AtclauseOrder 67 * </li> 68 * <li> 69 * JavadocBlockTagLocation 70 * </li> 71 * <li> 72 * JavadocMethod 73 * </li> 74 * <li> 75 * JavadocMissingLeadingAsterisk 76 * </li> 77 * <li> 78 * JavadocMissingWhitespaceAfterAsterisk 79 * </li> 80 * <li> 81 * JavadocParagraph 82 * </li> 83 * <li> 84 * JavadocStyle 85 * </li> 86 * <li> 87 * JavadocTagContinuationIndentation 88 * </li> 89 * <li> 90 * JavadocType 91 * </li> 92 * <li> 93 * MissingDeprecated 94 * </li> 95 * <li> 96 * NonEmptyAtclauseDescription 97 * </li> 98 * <li> 99 * 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 */ 205 public 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 }