1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.filters;
21
22 import java.util.Objects;
23 import java.util.regex.Pattern;
24
25 import javax.annotation.Nullable;
26
27 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
28 import com.puppycrawl.tools.checkstyle.api.Filter;
29
30 /**
31 * This filter element is immutable and processes {@link AuditEvent}
32 * objects based on the criteria of file, check, module id, line, and
33 * column. It rejects an AuditEvent if the following match:
34 * <ul>
35 * <li>the event's file name; and</li>
36 * <li>the check name or the module identifier; and</li>
37 * <li>(optionally) the event's line is in the filter's line CSV; and</li>
38 * <li>(optionally) the check's columns is in the filter's column CSV.</li>
39 * </ul>
40 *
41 */
42 public class SuppressFilterElement
43 implements Filter {
44
45 /** The regexp to match file names against. */
46 private final Pattern fileRegexp;
47
48 /** The regexp to match check names against. */
49 private final Pattern checkRegexp;
50
51 /** The regexp to match message names against. */
52 private final Pattern messageRegexp;
53
54 /** Module id filter. */
55 private final String moduleId;
56
57 /** Line number filter. */
58 private final CsvFilterElement lineFilter;
59
60 /** CSV for line number filter. */
61 private final String linesCsv;
62
63 /** Column number filter. */
64 private final CsvFilterElement columnFilter;
65
66 /** CSV for column number filter. */
67 private final String columnsCsv;
68
69 /**
70 * Creates a {@code SuppressFilterElement} instance.
71 *
72 * @param files regular expression for filtered file names
73 * @param checks regular expression for filtered check classes
74 * @param message regular expression for messages.
75 * @param moduleId the module id
76 * @param lines CSV for lines
77 * @param columns CSV for columns
78 */
79 public SuppressFilterElement(Pattern files, Pattern checks, Pattern message, String moduleId,
80 String lines, String columns) {
81 fileRegexp = files;
82 checkRegexp = checks;
83 messageRegexp = message;
84 this.moduleId = moduleId;
85 if (lines == null) {
86 linesCsv = null;
87 lineFilter = null;
88 }
89 else {
90 linesCsv = lines;
91 lineFilter = new CsvFilterElement(lines);
92 }
93 if (columns == null) {
94 columnsCsv = null;
95 columnFilter = null;
96 }
97 else {
98 columnsCsv = columns;
99 columnFilter = new CsvFilterElement(columns);
100 }
101 }
102
103 /**
104 * Constructs a {@code SuppressFilterElement} using regular expressions
105 * as {@code String}s. These are internally compiled into {@code Pattern}
106 * objects and passed to the main constructor.
107 *
108 * @param files regular expression for names of filtered files.
109 * @param checks regular expression for filtered check classes.
110 * @param message regular expression for messages.
111 * @param modId the id
112 * @param lines lines CSV values and ranges for line number filtering.
113 * @param columns columns CSV values and ranges for column number filtering.
114 */
115 public SuppressFilterElement(String files, String checks,
116 String message, String modId, String lines, String columns) {
117 this(toPattern(files), toPattern(checks), toPattern(message),
118 modId, lines, columns);
119 }
120
121 /**
122 * Converts a string into a compiled {@code Pattern}, or return {@code null}
123 * if input is {@code null}.
124 *
125 * @param regex the regular expression as a string, may be {@code null}.
126 * @return the compiled {@code Pattern}, or {@code null} if input is {@code null}.
127 */
128 private static Pattern toPattern(String regex) {
129 final Pattern result;
130 if (regex != null) {
131 result = Pattern.compile(regex);
132 }
133 else {
134 result = null;
135 }
136 return result;
137 }
138
139 @Override
140 public boolean accept(AuditEvent event) {
141 return !isFileNameAndModuleNameMatching(event)
142 || !isMessageNameMatching(event)
143 || !isLineAndColumnMatching(event);
144 }
145
146 /**
147 * Is matching by file name, module id, and Check name.
148 *
149 * @param event event
150 * @return true if it is matching
151 */
152 private boolean isFileNameAndModuleNameMatching(AuditEvent event) {
153 return event.getFileName() != null
154 && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
155 && event.getViolation() != null
156 && (moduleId == null || moduleId.equals(event.getModuleId()))
157 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
158 }
159
160 /**
161 * Is matching by message.
162 *
163 * @param event event
164 * @return true if it is matching or not set.
165 */
166 private boolean isMessageNameMatching(AuditEvent event) {
167 return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
168 }
169
170 /**
171 * Whether line and column match.
172 *
173 * @param event event to process.
174 * @return true if line and column are matching or not set.
175 */
176 private boolean isLineAndColumnMatching(AuditEvent event) {
177 return lineFilter == null && columnFilter == null
178 || lineFilter != null && lineFilter.accept(event.getLine())
179 || columnFilter != null && columnFilter.accept(event.getColumn());
180 }
181
182 @Override
183 public int hashCode() {
184 return Objects.hash(getPatternSafely(fileRegexp), getPatternSafely(checkRegexp),
185 getPatternSafely(messageRegexp), moduleId, linesCsv, columnsCsv);
186 }
187
188 @Override
189 public boolean equals(Object other) {
190 if (this == other) {
191 return true;
192 }
193 if (other == null || getClass() != other.getClass()) {
194 return false;
195 }
196 final SuppressFilterElement suppressElement = (SuppressFilterElement) other;
197 return Objects.equals(getPatternSafely(fileRegexp),
198 getPatternSafely(suppressElement.fileRegexp))
199 && Objects.equals(getPatternSafely(checkRegexp),
200 getPatternSafely(suppressElement.checkRegexp))
201 && Objects.equals(getPatternSafely(messageRegexp),
202 getPatternSafely(suppressElement.messageRegexp))
203 && Objects.equals(moduleId, suppressElement.moduleId)
204 && Objects.equals(linesCsv, suppressElement.linesCsv)
205 && Objects.equals(columnsCsv, suppressElement.columnsCsv);
206 }
207
208 /**
209 * Util method to get pattern String value from Pattern object safely, return null if
210 * pattern object is null.
211 *
212 * @param pattern pattern object
213 * @return value of pattern or null
214 */
215 @Nullable
216 private static String getPatternSafely(Pattern pattern) {
217 String result = null;
218 if (pattern != null) {
219 result = pattern.pattern();
220 }
221 return result;
222 }
223 }