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 * <p>
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 * </p>
043 * <p>
044 * Currently, filter does not support the following checks:
045 * </p>
046 * <ul id="IncompatibleChecks">
047 * <li>
048 * NoCodeInFile (reason is that AST is not generated for a file not containing code)
049 * </li>
050 * <li>
051 * Regexp (reason is at
052 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
053 * </li>
054 * <li>
055 * RegexpSinglelineJava (reason is at
056 * <a href="https://github.com/checkstyle/checkstyle/issues/7759#issuecomment-605525287"> #7759</a>)
057 * </li>
058 * </ul>
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 * JavadocStyle
083 * </li>
084 * <li>
085 * JavadocTagContinuationIndentation
086 * </li>
087 * <li>
088 * JavadocType
089 * </li>
090 * <li>
091 * MissingDeprecated
092 * </li>
093 * <li>
094 * NonEmptyAtclauseDescription
095 * </li>
096 * <li>
097 * RequireEmptyLineBeforeBlockTagGroup
098 * </li>
099 * <li>
100 * SingleLineJavadoc
101 * </li>
102 * <li>
103 * SummaryJavadoc
104 * </li>
105 * <li>
106 * WriteTag
107 * </li>
108 * </ul>
109 * <p>
110 * Note, that support for these Checks will be available after resolving issue
111 * <a href="https://github.com/checkstyle/checkstyle/issues/5770">#5770</a>.
112 * </p>
113 * <p>
114 * Currently, filter supports the following xpath axes:
115 * </p>
116 * <ul>
117 * <li>
118 * ancestor
119 * </li>
120 * <li>
121 * ancestor-or-self
122 * </li>
123 * <li>
124 * attribute
125 * </li>
126 * <li>
127 * child
128 * </li>
129 * <li>
130 * descendant
131 * </li>
132 * <li>
133 * descendant-or-self
134 * </li>
135 * <li>
136 * following
137 * </li>
138 * <li>
139 * following-sibling
140 * </li>
141 * <li>
142 * parent
143 * </li>
144 * <li>
145 * preceding
146 * </li>
147 * <li>
148 * preceding-sibling
149 * </li>
150 * <li>
151 * self
152 * </li>
153 * </ul>
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 * <p>
160 * The suppression file location is checked in following order:
161 * </p>
162 * <ol>
163 * <li>
164 * as a filesystem location
165 * </li>
166 * <li>
167 * if no file found, and the location starts with either {@code http://} or {@code https://},
168 * then it is interpreted as a URL
169 * </li>
170 * <li>
171 * if no file found, then passed to the {@code ClassLoader.getResource()} method.
172 * </li>
173 * </ol>
174 * <p>
175 * SuppressionXpathFilter can suppress Checks that have Treewalker as parent module.
176 * </p>
177 * <ul>
178 * <li>
179 * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
180 * Type is {@code java.lang.String}.
181 * Default value is {@code null}.
182 * </li>
183 * <li>
184 * Property {@code optional} - Control what to do when the file is not existing.
185 * If optional is set to false the file must exist, or else it ends with error.
186 * On the other hand if optional is true and file is not found, the filter accepts all audit events.
187 * Type is {@code boolean}.
188 * Default value is {@code false}.
189 * </li>
190 * </ul>
191 * <p>
192 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
193 * </p>
194 *
195 * @since 8.6
196 */
197public class SuppressionXpathFilter extends AbstractAutomaticBean implements
198        TreeWalkerFilter, ExternalResourceHolder {
199
200    /** Set of individual xpath suppresses. */
201    private final Set<TreeWalkerFilter> filters = new HashSet<>();
202
203    /** Specify the location of the <em>suppressions XML document</em> file. */
204    private String file;
205    /**
206     * Control what to do when the file is not existing.
207     * If optional is set to false the file must exist, or else it ends with error.
208     * On the other hand if optional is true and file is not found,
209     * the filter accepts all audit events.
210     */
211    private boolean optional;
212
213    /**
214     * Setter to specify the location of the <em>suppressions XML document</em> file.
215     *
216     * @param fileName name of the suppressions file.
217     * @since 8.6
218     */
219    public void setFile(String fileName) {
220        file = fileName;
221    }
222
223    /**
224     * Setter to control what to do when the file is not existing.
225     * If optional is set to false the file must exist, or else it ends with error.
226     * On the other hand if optional is true and file is not found,
227     * the filter accepts all audit events.
228     *
229     * @param optional tells if config file existence is optional.
230     * @since 8.6
231     */
232    public void setOptional(boolean optional) {
233        this.optional = optional;
234    }
235
236    @Override
237    public boolean equals(Object obj) {
238        if (this == obj) {
239            return true;
240        }
241        if (obj == null || getClass() != obj.getClass()) {
242            return false;
243        }
244        final SuppressionXpathFilter suppressionXpathFilter = (SuppressionXpathFilter) obj;
245        return Objects.equals(filters, suppressionXpathFilter.filters);
246    }
247
248    @Override
249    public int hashCode() {
250        return Objects.hash(filters);
251    }
252
253    @Override
254    public boolean accept(TreeWalkerAuditEvent treeWalkerAuditEvent) {
255        boolean result = true;
256        for (TreeWalkerFilter filter : filters) {
257            if (!filter.accept(treeWalkerAuditEvent)) {
258                result = false;
259                break;
260            }
261        }
262        return result;
263    }
264
265    @Override
266    public Set<String> getExternalResourceLocations() {
267        return Collections.singleton(file);
268    }
269
270    @Override
271    protected void finishLocalSetup() throws CheckstyleException {
272        if (file != null) {
273            if (optional) {
274                if (FilterUtil.isFileExists(file)) {
275                    filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
276                }
277            }
278            else {
279                filters.addAll(SuppressionsLoader.loadXpathSuppressions(file));
280            }
281        }
282    }
283
284}