001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 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.Objects;
023import java.util.regex.Pattern;
024
025import com.puppycrawl.tools.checkstyle.api.AuditEvent;
026import com.puppycrawl.tools.checkstyle.api.Filter;
027
028/**
029 * This filter element is immutable and processes {@link AuditEvent}
030 * objects based on the criteria of file, check, module id, line, and
031 * column. It rejects an AuditEvent if the following match:
032 * <ul>
033 *   <li>the event's file name; and</li>
034 *   <li>the check name or the module identifier; and</li>
035 *   <li>(optionally) the event's line is in the filter's line CSV; and</li>
036 *   <li>(optionally) the check's columns is in the filter's column CSV.</li>
037 * </ul>
038 *
039 */
040public class SuppressFilterElement
041    implements Filter {
042
043    /** The regexp to match file names against. */
044    private final Pattern fileRegexp;
045
046    /** The pattern for file names. */
047    private final String filePattern;
048
049    /** The regexp to match check names against. */
050    private final Pattern checkRegexp;
051
052    /** The pattern for check class names. */
053    private final String checkPattern;
054
055    /** The regexp to match message names against. */
056    private final Pattern messageRegexp;
057
058    /** The pattern for message names. */
059    private final String messagePattern;
060
061    /** Module id filter. */
062    private final String moduleId;
063
064    /** Line number filter. */
065    private final CsvFilterElement lineFilter;
066
067    /** CSV for line number filter. */
068    private final String linesCsv;
069
070    /** Column number filter. */
071    private final CsvFilterElement columnFilter;
072
073    /** CSV for column number filter. */
074    private final String columnsCsv;
075
076    /**
077     * Constructs a {@code SuppressFilterElement} for a
078     * file name pattern.
079     *
080     * @param files   regular expression for names of filtered files.
081     * @param checks  regular expression for filtered check classes.
082     * @param message regular expression for messages.
083     * @param modId   the id
084     * @param lines   lines CSV values and ranges for line number filtering.
085     * @param columns columns CSV values and ranges for column number filtering.
086     */
087    public SuppressFilterElement(String files, String checks,
088                           String message, String modId, String lines, String columns) {
089        filePattern = files;
090        if (files == null) {
091            fileRegexp = null;
092        }
093        else {
094            fileRegexp = Pattern.compile(files);
095        }
096        checkPattern = checks;
097        if (checks == null) {
098            checkRegexp = null;
099        }
100        else {
101            checkRegexp = Pattern.compile(checks);
102        }
103        messagePattern = message;
104        if (message == null) {
105            messageRegexp = null;
106        }
107        else {
108            messageRegexp = Pattern.compile(message);
109        }
110        moduleId = modId;
111        linesCsv = lines;
112        if (lines == null) {
113            lineFilter = null;
114        }
115        else {
116            lineFilter = new CsvFilterElement(lines);
117        }
118        columnsCsv = columns;
119        if (columns == null) {
120            columnFilter = null;
121        }
122        else {
123            columnFilter = new CsvFilterElement(columns);
124        }
125    }
126
127    /**
128     * Creates a {@code SuppressFilterElement} instance.
129     *
130     * @param files regular expression for filtered file names
131     * @param checks regular expression for filtered check classes
132     * @param message regular expression for messages.
133     * @param moduleId the module id
134     * @param lines CSV for lines
135     * @param columns CSV for columns
136     */
137    public SuppressFilterElement(Pattern files, Pattern checks, Pattern message, String moduleId,
138            String lines, String columns) {
139        if (files == null) {
140            filePattern = null;
141            fileRegexp = null;
142        }
143        else {
144            filePattern = files.pattern();
145            fileRegexp = files;
146        }
147        if (checks == null) {
148            checkPattern = null;
149            checkRegexp = null;
150        }
151        else {
152            checkPattern = checks.pattern();
153            checkRegexp = checks;
154        }
155        if (message == null) {
156            messagePattern = null;
157            messageRegexp = null;
158        }
159        else {
160            messagePattern = message.pattern();
161            messageRegexp = message;
162        }
163        this.moduleId = moduleId;
164        if (lines == null) {
165            linesCsv = null;
166            lineFilter = null;
167        }
168        else {
169            linesCsv = lines;
170            lineFilter = new CsvFilterElement(lines);
171        }
172        if (columns == null) {
173            columnsCsv = null;
174            columnFilter = null;
175        }
176        else {
177            columnsCsv = columns;
178            columnFilter = new CsvFilterElement(columns);
179        }
180    }
181
182    @Override
183    public boolean accept(AuditEvent event) {
184        return !isFileNameAndModuleNameMatching(event)
185                || !isMessageNameMatching(event)
186                || !isLineAndColumnMatching(event);
187    }
188
189    /**
190     * Is matching by file name, module id, and Check name.
191     *
192     * @param event event
193     * @return true if it is matching
194     */
195    private boolean isFileNameAndModuleNameMatching(AuditEvent event) {
196        return event.getFileName() != null
197                && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
198                && event.getViolation() != null
199                && (moduleId == null || moduleId.equals(event.getModuleId()))
200                && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
201    }
202
203    /**
204     * Is matching by message.
205     *
206     * @param event event
207     * @return true if it is matching or not set.
208     */
209    private boolean isMessageNameMatching(AuditEvent event) {
210        return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
211    }
212
213    /**
214     * Whether line and column match.
215     *
216     * @param event event to process.
217     * @return true if line and column are matching or not set.
218     */
219    private boolean isLineAndColumnMatching(AuditEvent event) {
220        return lineFilter == null && columnFilter == null
221                || lineFilter != null && lineFilter.accept(event.getLine())
222                || columnFilter != null && columnFilter.accept(event.getColumn());
223    }
224
225    @Override
226    public int hashCode() {
227        return Objects.hash(filePattern, checkPattern, messagePattern, moduleId, linesCsv,
228                columnsCsv);
229    }
230
231    @Override
232    public boolean equals(Object other) {
233        if (this == other) {
234            return true;
235        }
236        if (other == null || getClass() != other.getClass()) {
237            return false;
238        }
239        final SuppressFilterElement suppressElement = (SuppressFilterElement) other;
240        return Objects.equals(filePattern, suppressElement.filePattern)
241                && Objects.equals(checkPattern, suppressElement.checkPattern)
242                && Objects.equals(messagePattern, suppressElement.messagePattern)
243                && Objects.equals(moduleId, suppressElement.moduleId)
244                && Objects.equals(linesCsv, suppressElement.linesCsv)
245                && Objects.equals(columnsCsv, suppressElement.columnsCsv);
246    }
247
248}