SuppressionXpathFilter.java
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2024 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///////////////////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.filters;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean;
import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
import com.puppycrawl.tools.checkstyle.utils.FilterUtil;
/**
* <p>
* Filter {@code SuppressionXpathFilter} works as
* <a href="https://checkstyle.org/filters/suppressionfilter.html#SuppressionFilter">
* SuppressionFilter</a>.
* Additionally, filter processes {@code suppress-xpath} elements,
* which contains xpath-expressions. Xpath-expressions are queries for
* suppressed nodes inside the AST tree.
* </p>
* <p>
* Currently, filter does not support the following checks:
* </p>
* <ul id="IncompatibleChecks">
* <li>
* NoCodeInFile (reason is that AST is not generated for a file not containing code)
* </li>
* <li>
* Regexp (reason is at
* <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
* </li>
* <li>
* RegexpSinglelineJava (reason is at
* <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
* </li>
* </ul>
* <p>
* Also, the filter does not support suppressions inside javadoc reported by Javadoc checks:
* </p>
* <ul id="JavadocChecks">
* <li>
* AtclauseOrder
* </li>
* <li>
* JavadocBlockTagLocation
* </li>
* <li>
* JavadocMethod
* </li>
* <li>
* JavadocMissingLeadingAsterisk
* </li>
* <li>
* JavadocMissingWhitespaceAfterAsterisk
* </li>
* <li>
* JavadocParagraph
* </li>
* <li>
* JavadocStyle
* </li>
* <li>
* JavadocTagContinuationIndentation
* </li>
* <li>
* JavadocType
* </li>
* <li>
* MissingDeprecated
* </li>
* <li>
* NonEmptyAtclauseDescription
* </li>
* <li>
* RequireEmptyLineBeforeBlockTagGroup
* </li>
* <li>
* SingleLineJavadoc
* </li>
* <li>
* SummaryJavadoc
* </li>
* <li>
* WriteTag
* </li>
* </ul>
* <p>
* Note, that support for these Checks will be available after resolving issue
* <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a>.
* </p>
* <p>
* Currently, filter supports the following xpath axes:
* </p>
* <ul>
* <li>
* ancestor
* </li>
* <li>
* ancestor-or-self
* </li>
* <li>
* attribute
* </li>
* <li>
* child
* </li>
* <li>
* descendant
* </li>
* <li>
* descendant-or-self
* </li>
* <li>
* following
* </li>
* <li>
* following-sibling
* </li>
* <li>
* parent
* </li>
* <li>
* preceding
* </li>
* <li>
* preceding-sibling
* </li>
* <li>
* self
* </li>
* </ul>
* <p>
* You can use the command line helper tool to generate xpath suppressions based on your
* configuration file and input files. See <a href="https://checkstyle.org/cmdline.html">here</a>
* for more details.
* </p>
* <p>
* The suppression file location is checked in following order:
* </p>
* <ol>
* <li>
* as a filesystem location
* </li>
* <li>
* if no file found, and the location starts with either {@code http://} or {@code https://},
* then it is interpreted as a URL
* </li>
* <li>
* if no file found, then passed to the {@code ClassLoader.getResource()} method.
* </li>
* </ol>
* <p>
* SuppressionXpathFilter can suppress Checks that have Treewalker as parent module.
* </p>
* <ul>
* <li>
* Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
* Type is {@code java.lang.String}.
* Default value is {@code null}.
* </li>
* <li>
* Property {@code optional} - Control what to do when the file is not existing.
* If optional is set to false the file must exist, or else it ends with error.
* On the other hand if optional is true and file is not found, the filter accepts all audit events.
* Type is {@code boolean}.
* Default value is {@code false}.
* </li>
* </ul>
* <p>
* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
* </p>
*
* @since 8.6
*/
public class SuppressionXpathFilter extends AbstractAutomaticBean implements
TreeWalkerFilter, ExternalResourceHolder {
/** Set of individual xpath suppresses. */
private final Set<TreeWalkerFilter> filters = new HashSet<>();
/** Specify the location of the <em>suppressions XML document</em> file. */
private String file;
/**
* Control what to do when the file is not existing.
* If optional is set to false the file must exist, or else it ends with error.
* On the other hand if optional is true and file is not found,
* the filter accepts all audit events.
*/
private boolean optional;
/**
* Setter to specify the location of the <em>suppressions XML document</em> file.
*
* @param fileName name of the suppressions file.
* @since 8.6
*/
public void setFile(String fileName) {
file = fileName;
}
/**
* Setter to control what to do when the file is not existing.
* If optional is set to false the file must exist, or else it ends with error.
* On the other hand if optional is true and file is not found,
* the filter accepts all audit events.
*
* @param optional tells if config file existence is optional.
* @since 8.6
*/
public void setOptional(boolean optional) {
this.optional = optional;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
return Objects.equals(filters, suppressionXpathFilter.filters);
}
@Override
public int hashCode() {
return Objects.hash(filters);
}
@Override
public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
boolean result = true;
for (TreeWalkerFilter filter : filters) {
if (!filter.accept(treeWalkerAuditEvent)) {
result = false;
break;
}
}
return result;
}
@Override
public Set<String> getExternalResourceLocations() {
return Collections.singleton(file);
}
@Override
protected void finishLocalSetup() throws CheckstyleException {
if (file != null) {
if (optional) {
if (FilterUtil.isFileExists(file)) {
filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
}
}
else {
filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
}
}
}
}