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.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     * @param files regular expression for filtered file names
130     * @param checks regular expression for filtered check classes
131     * @param message regular expression for messages.
132     * @param moduleId the module id
133     * @param lines CSV for lines
134     * @param columns CSV for columns
135     */
136    public SuppressFilterElement(Pattern files, Pattern checks, Pattern message, String moduleId,
137            String lines, String columns) {
138        if (files == null) {
139            filePattern = null;
140            fileRegexp = null;
141        }
142        else {
143            filePattern = files.pattern();
144            fileRegexp = files;
145        }
146        if (checks == null) {
147            checkPattern = null;
148            checkRegexp = null;
149        }
150        else {
151            checkPattern = checks.pattern();
152            checkRegexp = checks;
153        }
154        if (message == null) {
155            messagePattern = null;
156            messageRegexp = null;
157        }
158        else {
159            messagePattern = message.pattern();
160            messageRegexp = message;
161        }
162        this.moduleId = moduleId;
163        if (lines == null) {
164            linesCsv = null;
165            lineFilter = null;
166        }
167        else {
168            linesCsv = lines;
169            lineFilter = new CsvFilterElement(lines);
170        }
171        if (columns == null) {
172            columnsCsv = null;
173            columnFilter = null;
174        }
175        else {
176            columnsCsv = columns;
177            columnFilter = new CsvFilterElement(columns);
178        }
179    }
180
181    @Override
182    public boolean accept(AuditEvent event) {
183        return !isFileNameAndModuleNameMatching(event)
184                || !isMessageNameMatching(event)
185                || !isLineAndColumnMatching(event);
186    }
187
188    /**
189     * Is matching by file name, module id, and Check name.
190     * @param event event
191     * @return true if it is matching
192     */
193    private boolean isFileNameAndModuleNameMatching(AuditEvent event) {
194        return event.getFileName() != null
195                && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
196                && event.getLocalizedMessage() != null
197                && (moduleId == null || moduleId.equals(event.getModuleId()))
198                && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
199    }
200
201    /**
202     * Is matching by message.
203     * @param event event
204     * @return true if it is matching or not set.
205     */
206    private boolean isMessageNameMatching(AuditEvent event) {
207        return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
208    }
209
210    /**
211     * Whether line and column match.
212     * @param event event to process.
213     * @return true if line and column are matching or not set.
214     */
215    private boolean isLineAndColumnMatching(AuditEvent event) {
216        return lineFilter == null && columnFilter == null
217                || lineFilter != null && lineFilter.accept(event.getLine())
218                || columnFilter != null && columnFilter.accept(event.getColumn());
219    }
220
221    @Override
222    public int hashCode() {
223        return Objects.hash(filePattern, checkPattern, messagePattern, moduleId, linesCsv,
224                columnsCsv);
225    }
226
227    @Override
228    public boolean equals(Object other) {
229        if (this == other) {
230            return true;
231        }
232        if (other == null || getClass() != other.getClass()) {
233            return false;
234        }
235        final SuppressFilterElement suppressElement = (SuppressFilterElement) other;
236        return Objects.equals(filePattern, suppressElement.filePattern)
237                && Objects.equals(checkPattern, suppressElement.checkPattern)
238                && Objects.equals(messagePattern, suppressElement.messagePattern)
239                && Objects.equals(moduleId, suppressElement.moduleId)
240                && Objects.equals(linesCsv, suppressElement.linesCsv)
241                && Objects.equals(columnsCsv, suppressElement.columnsCsv);
242    }
243
244}