001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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.Set;
023
024import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean;
025import com.puppycrawl.tools.checkstyle.api.AuditEvent;
026import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
027import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
028import com.puppycrawl.tools.checkstyle.api.Filter;
029import com.puppycrawl.tools.checkstyle.api.FilterSet;
030import com.puppycrawl.tools.checkstyle.utils.FilterUtil;
031import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
032
033/**
034 * <div>
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 * </div>
040 *
041 * <p>
042 * Notes:
043 * A <a href="https://checkstyle.org/dtds/suppressions_1_2.dtd">suppressions XML document</a>
044 * contains a set of {@code suppress} elements, where each {@code suppress}
045 * element can have the following attributes:
046 * </p>
047 * <ul>
048 * <li>
049 * {@code files} - a <a href="https://checkstyle.org/property_types.html#Pattern">
050 * Pattern</a> matched against the file name associated with an audit event.
051 * It is optional.
052 * </li>
053 * <li>
054 * {@code checks} - a <a href="https://checkstyle.org/property_types.html#Pattern">
055 * Pattern</a> matched against the name of the check associated with an audit event.
056 * Optional as long as {@code id} or {@code message} is specified.
057 * </li>
058 * <li>
059 * {@code message} - a <a href="https://checkstyle.org/property_types.html#Pattern">
060 * Pattern</a> matched against the message of the check associated with an audit event.
061 * Optional as long as {@code checks} or {@code id} is specified.
062 * </li>
063 * <li>
064 * {@code id} - a <a href="https://checkstyle.org/property_types.html#String">String</a>
065 * matched against the <a href="https://checkstyle.org/config.html#Id">check id</a>
066 * associated with an audit event.
067 * Optional as long as {@code checks} or {@code message} is specified.
068 * </li>
069 * <li>
070 * {@code lines} - a comma-separated list of values, where each value is an
071 * <a href="https://checkstyle.org/property_types.html#int">int</a>
072 * or a range of integers denoted by integer-integer.
073 * It is optional.
074 * </li>
075 * <li>
076 * {@code columns} - a comma-separated list of values, where each value is an
077 * <a href="https://checkstyle.org/property_types.html#int">int</a>
078 * or a range of integers denoted by integer-integer.
079 * It is optional.
080 * </li>
081 * </ul>
082 *
083 * <p>
084 * Each audit event is checked against each {@code suppress} element.
085 * It is suppressed if all specified attributes match against the audit event.
086 * </p>
087 *
088 * <p>
089 * ATTENTION: filtering by message is dependent on runtime locale.
090 * If project is running in different languages it is better to avoid filtering by message.
091 * </p>
092 *
093 * <p>
094 * You can download template of empty suppression filter
095 * <a href="https://checkstyle.org/files/suppressions_none.xml">here</a>.
096 * </p>
097 *
098 * <p>
099 * Location of the file defined in {@code file} property is checked in the following order:
100 * </p>
101 * <ol>
102 * <li>
103 * as a filesystem location
104 * </li>
105 * <li>
106 * if no file found, and the location starts with either {@code http://} or {@code https://},
107 * then it is interpreted as a URL
108 * </li>
109 * <li>
110 * if no file found, then passed to the {@code ClassLoader.getResource()} method.
111 * </li>
112 * </ol>
113 *
114 * <p>
115 * SuppressionFilter can suppress Checks that have Treewalker or Checker as parent module.
116 * </p>
117 * <ul>
118 * <li>
119 * Property {@code file} - Specify the location of the <em>suppressions XML document</em> file.
120 * Type is {@code java.lang.String}.
121 * Default value is {@code null}.
122 * </li>
123 * <li>
124 * Property {@code optional} - Control what to do when the file is not existing.
125 * If {@code optional} is set to {@code false} the file must exist, or else it
126 * ends with error. On the other hand if optional is {@code true} and file is
127 * not found, the filter accept all audit events.
128 * Type is {@code boolean}.
129 * Default value is {@code false}.
130 * </li>
131 * </ul>
132 *
133 * <p>
134 * Parent is {@code com.puppycrawl.tools.checkstyle.Checker}
135 * </p>
136 *
137 * @since 3.2
138 */
139public class SuppressionFilter
140        extends AbstractAutomaticBean
141        implements Filter, ExternalResourceHolder {
142
143    /** Specify the location of the <em>suppressions XML document</em> file. */
144    private String file;
145    /**
146     * Control what to do when the file is not existing. If {@code optional} is
147     * set to {@code false} the file must exist, or else it ends with error.
148     * On the other hand if optional is {@code true} and file is not found,
149     * the filter accept all audit events.
150     */
151    private boolean optional;
152    /** Set of individual suppresses. */
153    private FilterSet filters = new FilterSet();
154
155    /**
156     * Setter to specify the location of the <em>suppressions XML document</em> file.
157     *
158     * @param fileName name of the suppressions file.
159     * @since 3.2
160     */
161    public void setFile(String fileName) {
162        file = fileName;
163    }
164
165    /**
166     * Setter to control what to do when the file is not existing.
167     * If {@code optional} is set to {@code false} the file must exist, or else
168     * it ends with error. On the other hand if optional is {@code true}
169     * and file is not found, the filter accept all audit events.
170     *
171     * @param optional tells if config file existence is optional.
172     * @since 6.15
173     */
174    public void setOptional(boolean optional) {
175        this.optional = optional;
176    }
177
178    @Override
179    public boolean accept(AuditEvent event) {
180        return filters.accept(event);
181    }
182
183    @Override
184    protected void finishLocalSetup() throws CheckstyleException {
185        if (file != null) {
186            if (optional) {
187                if (FilterUtil.isFileExists(file)) {
188                    filters = SuppressionsLoader.loadSuppressions(file);
189                }
190            }
191            else {
192                filters = SuppressionsLoader.loadSuppressions(file);
193            }
194        }
195    }
196
197    @Override
198    public Set<String> getExternalResourceLocations() {
199        return UnmodifiableCollectionUtil.singleton(file);
200    }
201
202}