001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.Set;
024
025import com.puppycrawl.tools.checkstyle.api.AuditEvent;
026import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
027import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
028import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
029import com.puppycrawl.tools.checkstyle.api.Filter;
030import com.puppycrawl.tools.checkstyle.api.FilterSet;
031import com.puppycrawl.tools.checkstyle.utils.FilterUtil;
032
033/**
034 * <p>
035 * Filter {@code SuppressionFilter} rejects audit events for Check violations according to a
036 * <a href="https://checkstyle.org/dtds/suppressions_1_2.dtd">suppressions XML document</a>
037 * in a file. If there is no configured suppressions file or the optional is set to true and
038 * suppressions file was not found the Filter accepts all audit events.
039 * </p>
040 * <p>
041 * A <a href="https://checkstyle.org/dtds/suppressions_1_2.dtd">suppressions XML document</a>
042 * contains a set of {@code suppress} elements, where each {@code suppress}
043 * element can have the following attributes:
044 * </p>
045 * <ul>
046 * <li>
047 * {@code files} - a <a href="https://checkstyle.org/property_types.html#regexp">
048 * Regular Expression</a> matched against the file name associated with an audit event.
049 * It is optional.
050 * </li>
051 * <li>
052 * {@code checks} - a <a href="https://checkstyle.org/property_types.html#regexp">
053 * Regular Expression</a> matched against the name of the check associated with an audit event.
054 * Optional as long as {@code id} or {@code message} is specified.
055 * </li>
056 * <li>
057 * {@code message} - a <a href="https://checkstyle.org/property_types.html#regexp">
058 * Regular Expression</a> matched against the message of the check associated with an audit event.
059 * Optional as long as {@code checks} or {@code id} is specified.
060 * </li>
061 * <li>
062 * {@code id} - a <a href="https://checkstyle.org/property_types.html#string">string</a>
063 * matched against the <a href="https://checkstyle.org/config.html#Id">check id</a>
064 * associated with an audit event.
065 * Optional as long as {@code checks} or {@code message} is specified.
066 * </li>
067 * <li>
068 * {@code lines} - a comma-separated list of values, where each value is an
069 * <a href="https://checkstyle.org/property_types.html#integer">integer</a>
070 * or a range of integers denoted by integer-integer.
071 * It is optional.
072 * </li>
073 * <li>
074 * {@code columns} - a comma-separated list of values, where each value is an
075 * <a href="https://checkstyle.org/property_types.html#integer">integer</a>
076 * or a range of integers denoted by integer-integer.
077 * It is optional.
078 * </li>
079 * </ul>
080 * <p>
081 * Each audit event is checked against each {@code suppress} element.
082 * It is suppressed if all specified attributes match against the audit event.
083 * </p>
084 * <p>
085 * ATTENTION: filtering by message is dependant on runtime locale.
086 * If project is running in different languages it is better to avoid filtering by message.
087 * </p>
088 * <p>
089 * You can download template of empty suppression filter
090 * <a href="https://checkstyle.org/files/suppressions_none.xml">here</a>.
091 * </p>
092 * <p>
093 * Location of the file defined in {@code file} property is checked in the following order:
094 * </p>
095 * <ol>
096 * <li>
097 * as a filesystem location
098 * </li>
099 * <li>
100 * if no file found, and the location starts with either {@code http://} or {@code https://},
101 * then it is interpreted as a URL
102 * </li>
103 * <li>
104 * if no file found, then passed to the {@code ClassLoader.getResource()} method.
105 * </li>
106 * </ol>
107 * <ul>
108 * <li>
109 * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
110 * Default value is {@code null}.
111 * </li>
112 * <li>
113 * Property {@code optional} - Control what to do when the file is not existing.
114 * If {@code optional} is set to {@code false} the file must exist, or else it
115 * ends with error. On the other hand if optional is {@code true} and file is
116 * not found, the filter accept all audit events.
117 * Default value is {@code false}.
118 * </li>
119 * </ul>
120 * <p>
121 * For example, the following configuration fragment directs the Checker to use
122 * a {@code SuppressionFilter} with suppressions file {@code config/suppressions.xml}:
123 * </p>
124 * <pre>
125 * &lt;module name=&quot;SuppressionFilter&quot;&gt;
126 *   &lt;property name=&quot;file&quot; value=&quot;config/suppressions.xml&quot;/&gt;
127 *   &lt;property name=&quot;optional&quot; value=&quot;false&quot;/&gt;
128 * &lt;/module&gt;
129 * </pre>
130 * <p>
131 * The following suppressions XML document directs a {@code SuppressionFilter} to
132 * reject {@code JavadocStyleCheck} violations for lines 82 and 108 to 122 of file
133 * {@code AbstractComplexityCheck.java}, and {@code MagicNumberCheck} violations for
134 * line 221 of file {@code JavadocStyleCheck.java}, and
135 * {@code 'Missing a Javadoc comment'} violations for all lines and files:
136 * </p>
137 * <pre>
138 * &lt;?xml version=&quot;1.0&quot;?&gt;
139 *
140 * &lt;!DOCTYPE suppressions PUBLIC
141 *   &quot;-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN&quot;
142 *   &quot;https://checkstyle.org/dtds/suppressions_1_2.dtd&quot;&gt;
143 *
144 * &lt;suppressions&gt;
145 *   &lt;suppress checks=&quot;JavadocStyleCheck&quot;
146 *     files=&quot;AbstractComplexityCheck.java&quot;
147 *     lines=&quot;82,108-122&quot;/&gt;
148 *   &lt;suppress checks=&quot;MagicNumberCheck&quot;
149 *     files=&quot;JavadocStyleCheck.java&quot;
150 *     lines=&quot;221&quot;/&gt;
151 *   &lt;suppress message=&quot;Missing a Javadoc comment&quot;/&gt;
152 * &lt;/suppressions&gt;
153 * </pre>
154 * <p>
155 * Suppress check by <a href="https://checkstyle.org/config.html#Id">module id</a>
156 * when config have two instances on the same check:
157 * </p>
158 * <pre>
159 * &lt;suppress id=&quot;stringEqual&quot; files=&quot;SomeTestCode.java&quot;/&gt;
160 * </pre>
161 * <p>
162 * Suppress all checks for hidden files and folders:
163 * </p>
164 * <pre>
165 * &lt;suppress files=&quot;[/\\]\..+&quot; checks=&quot;.*&quot;/&gt;
166 * </pre>
167 * <p>
168 * Suppress all checks for Maven-generated code:
169 * </p>
170 * <pre>
171 * &lt;suppress files=&quot;[/\\]target[/\\]&quot; checks=&quot;.*&quot;/&gt;
172 * </pre>
173 * <p>
174 * Suppress all checks for archives, classes and other binary files:
175 * </p>
176 * <pre>
177 * &lt;suppress files=&quot;.+\.(?:jar|zip|war|class|tar|bin)$&quot; checks=&quot;.*&quot;/&gt;
178 * </pre>
179 * <p>
180 * Suppress all checks for image files:
181 * </p>
182 * <pre>
183 * &lt;suppress files=".+\.(?:png|gif|jpg|jpeg)$" checks=".*"/&gt;
184 * </pre>
185 * <p>
186 * Suppress all checks for non-java files:
187 * </p>
188 * <pre>
189 * &lt;suppress files=&quot;.+\.(?:txt|xml|csv|sh|thrift|html|sql|eot|ttf|woff|css|png)$&quot;
190 *     checks=&quot;.*&quot;/&gt;
191 * </pre>
192 * <p>
193 * Suppress all checks in generated sources:
194 * </p>
195 * <pre>
196 * &lt;suppress checks=&quot;.*&quot; files=&quot;com[\\/]mycompany[\\/]app[\\/]gen[\\/]&quot;/&gt;
197 * </pre>
198 * <p>
199 * Suppress FileLength check on integration tests in certain folder:
200 * </p>
201 * <pre>
202 * &lt;suppress checks=&quot;FileLength&quot;
203 *   files=&quot;com[\\/]mycompany[\\/]app[\\/].*IT.java&quot;/&gt;
204 * </pre>
205 * <p>
206 * Suppress naming violations on variable named 'log' in all files:
207 * </p>
208 * <pre>
209 * &lt;suppress message=&quot;Name 'log' must match pattern&quot;/&gt;
210 * </pre>
211 *
212 * @since 3.2
213 */
214public class SuppressionFilter extends AutomaticBean implements Filter, ExternalResourceHolder {
215
216    /** Specify the location of the <em>suppressions XML document</em> file. */
217    private String file;
218    /**
219     * Control what to do when the file is not existing. If {@code optional} is
220     * set to {@code false} the file must exist, or else it ends with error.
221     * On the other hand if optional is {@code true} and file is not found,
222     * the filter accept all audit events.
223     */
224    private boolean optional;
225    /** Set of individual suppresses. */
226    private FilterSet filters = new FilterSet();
227
228    /**
229     * Setter to specify the location of the <em>suppressions XML document</em> file.
230     * @param fileName name of the suppressions file.
231     */
232    public void setFile(String fileName) {
233        file = fileName;
234    }
235
236    /**
237     * Setter to control what to do when the file is not existing.
238     * If {@code optional} is set to {@code false} the file must exist, or else
239     * it ends with error. On the other hand if optional is {@code true}
240     * and file is not found, the filter accept all audit events.
241     * @param optional tells if config file existence is optional.
242     */
243    public void setOptional(boolean optional) {
244        this.optional = optional;
245    }
246
247    @Override
248    public boolean accept(AuditEvent event) {
249        return filters.accept(event);
250    }
251
252    @Override
253    protected void finishLocalSetup() throws CheckstyleException {
254        if (file != null) {
255            if (optional) {
256                if (FilterUtil.isFileExists(file)) {
257                    filters = SuppressionsLoader.loadSuppressions(file);
258                }
259                else {
260                    filters = new FilterSet();
261                }
262            }
263            else {
264                filters = SuppressionsLoader.loadSuppressions(file);
265            }
266        }
267    }
268
269    @Override
270    public Set<String> getExternalResourceLocations() {
271        return Collections.singleton(file);
272    }
273
274}