1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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 com.puppycrawl.tools.checkstyle.api.AuditEvent;
26 import com.puppycrawl.tools.checkstyle.api.Filter;
27
28 /**
29 * This filter element is immutable and processes {@link AuditEvent}
30 * objects based on the criteria of file, check, module id, line, and
31 * column. It rejects an AuditEvent if the following match:
32 * <ul>
33 * <li>the event's file name; and</li>
34 * <li>the check name or the module identifier; and</li>
35 * <li>(optionally) the event's line is in the filter's line CSV; and</li>
36 * <li>(optionally) the check's columns is in the filter's column CSV.</li>
37 * </ul>
38 *
39 */
40 public class SuppressFilterElement
41 implements Filter {
42
43 /** The regexp to match file names against. */
44 private final Pattern fileRegexp;
45
46 /** The regexp to match check names against. */
47 private final Pattern checkRegexp;
48
49 /** The regexp to match message names against. */
50 private final Pattern messageRegexp;
51
52 /** Module id filter. */
53 private final String moduleId;
54
55 /** Line number filter. */
56 private final CsvFilterElement lineFilter;
57
58 /** CSV for line number filter. */
59 private final String linesCsv;
60
61 /** Column number filter. */
62 private final CsvFilterElement columnFilter;
63
64 /** CSV for column number filter. */
65 private final String columnsCsv;
66
67 /**
68 * Creates a {@code SuppressFilterElement} instance.
69 *
70 * @param files regular expression for filtered file names
71 * @param checks regular expression for filtered check classes
72 * @param message regular expression for messages.
73 * @param moduleId the module id
74 * @param lines CSV for lines
75 * @param columns CSV for columns
76 */
77 public SuppressFilterElement(Pattern files, Pattern checks, Pattern message, String moduleId,
78 String lines, String columns) {
79 fileRegexp = files;
80 checkRegexp = checks;
81 messageRegexp = message;
82 this.moduleId = moduleId;
83 if (lines == null) {
84 linesCsv = null;
85 lineFilter = null;
86 }
87 else {
88 linesCsv = lines;
89 lineFilter = new CsvFilterElement(lines);
90 }
91 if (columns == null) {
92 columnsCsv = null;
93 columnFilter = null;
94 }
95 else {
96 columnsCsv = columns;
97 columnFilter = new CsvFilterElement(columns);
98 }
99 }
100
101 /**
102 * Constructs a {@code SuppressFilterElement} using regular expressions
103 * as {@code String}s. These are internally compiled into {@code Pattern}
104 * objects and passed to the main constructor.
105 *
106 * @param files regular expression for names of filtered files.
107 * @param checks regular expression for filtered check classes.
108 * @param message regular expression for messages.
109 * @param modId the id
110 * @param lines lines CSV values and ranges for line number filtering.
111 * @param columns columns CSV values and ranges for column number filtering.
112 */
113 public SuppressFilterElement(String files, String checks,
114 String message, String modId, String lines, String columns) {
115 this(toPattern(files), toPattern(checks), toPattern(message),
116 modId, lines, columns);
117 }
118
119 /**
120 * Converts a string into a compiled {@code Pattern}, or return {@code null}
121 * if input is {@code null}.
122 *
123 * @param regex the regular expression as a string, may be {@code null}.
124 * @return the compiled {@code Pattern}, or {@code null} if input is {@code null}.
125 */
126 private static Pattern toPattern(String regex) {
127 final Pattern result;
128 if (regex != null) {
129 result = Pattern.compile(regex);
130 }
131 else {
132 result = null;
133 }
134 return result;
135 }
136
137 @Override
138 public boolean accept(AuditEvent event) {
139 return !isFileNameAndModuleNameMatching(event)
140 || !isMessageNameMatching(event)
141 || !isLineAndColumnMatching(event);
142 }
143
144 /**
145 * Is matching by file name, module id, and Check name.
146 *
147 * @param event event
148 * @return true if it is matching
149 */
150 private boolean isFileNameAndModuleNameMatching(AuditEvent event) {
151 return event.getFileName() != null
152 && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
153 && event.getViolation() != null
154 && (moduleId == null || moduleId.equals(event.getModuleId()))
155 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
156 }
157
158 /**
159 * Is matching by message.
160 *
161 * @param event event
162 * @return true if it is matching or not set.
163 */
164 private boolean isMessageNameMatching(AuditEvent event) {
165 return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
166 }
167
168 /**
169 * Whether line and column match.
170 *
171 * @param event event to process.
172 * @return true if line and column are matching or not set.
173 */
174 private boolean isLineAndColumnMatching(AuditEvent event) {
175 return lineFilter == null && columnFilter == null
176 || lineFilter != null && lineFilter.accept(event.getLine())
177 || columnFilter != null && columnFilter.accept(event.getColumn());
178 }
179
180 @Override
181 public int hashCode() {
182 return Objects.hash(getPatternSafely(fileRegexp), getPatternSafely(checkRegexp),
183 getPatternSafely(messageRegexp), moduleId, linesCsv, columnsCsv);
184 }
185
186 @Override
187 public boolean equals(Object other) {
188 if (this == other) {
189 return true;
190 }
191 if (other == null || getClass() != other.getClass()) {
192 return false;
193 }
194 final SuppressFilterElement suppressElement = (SuppressFilterElement) other;
195 return Objects.equals(getPatternSafely(fileRegexp),
196 getPatternSafely(suppressElement.fileRegexp))
197 && Objects.equals(getPatternSafely(checkRegexp),
198 getPatternSafely(suppressElement.checkRegexp))
199 && Objects.equals(getPatternSafely(messageRegexp),
200 getPatternSafely(suppressElement.messageRegexp))
201 && Objects.equals(moduleId, suppressElement.moduleId)
202 && Objects.equals(linesCsv, suppressElement.linesCsv)
203 && Objects.equals(columnsCsv, suppressElement.columnsCsv);
204 }
205
206 /**
207 * Util method to get pattern String value from Pattern object safely, return null if
208 * pattern object is null.
209 *
210 * @param pattern pattern object
211 * @return value of pattern or null
212 */
213 private static String getPatternSafely(Pattern pattern) {
214 String result = null;
215 if (pattern != null) {
216 result = pattern.pattern();
217 }
218 return result;
219 }
220 }