View Javadoc
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 || isFileMatch(event.getFileName()))
155                 && event.getViolation() != null
156                 && (moduleId == null || moduleId.equals(event.getModuleId()))
157                 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
158     }
159 
160     /**
161      * Checks if the given file name matches the file regexp.
162      * If there is no match and the OS uses backslashes (Windows),
163      * it converts backslashes to forward slashes and tries again.
164      *
165      * @param fileName the name of the file to check
166      * @return true if the file matches the regexp
167      */
168     private boolean isFileMatch(String fileName) {
169         boolean match = fileRegexp.matcher(fileName).find();
170         if (!match) {
171             final String slashesFileName = fileName.replace('\\', '/');
172             match = fileRegexp.matcher(slashesFileName).find();
173         }
174         return match;
175     }
176 
177     /**
178      * Is matching by message.
179      *
180      * @param event event
181      * @return true if it is matching or not set.
182      */
183     private boolean isMessageNameMatching(AuditEvent event) {
184         return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
185     }
186 
187     /**
188      * Whether line and column match.
189      *
190      * @param event event to process.
191      * @return true if line and column are matching or not set.
192      */
193     private boolean isLineAndColumnMatching(AuditEvent event) {
194         return lineFilter == null && columnFilter == null
195                 || lineFilter != null && lineFilter.accept(event.getLine())
196                 || columnFilter != null && columnFilter.accept(event.getColumn());
197     }
198 
199     @Override
200     public int hashCode() {
201         return Objects.hash(getPatternSafely(fileRegexp), getPatternSafely(checkRegexp),
202                 getPatternSafely(messageRegexp), moduleId, linesCsv, columnsCsv);
203     }
204 
205     @Override
206     public boolean equals(Object other) {
207         if (this == other) {
208             return true;
209         }
210         if (other == null || getClass() != other.getClass()) {
211             return false;
212         }
213         final SuppressFilterElement suppressElement = (SuppressFilterElement) other;
214         return Objects.equals(getPatternSafely(fileRegexp),
215                     getPatternSafely(suppressElement.fileRegexp))
216                 && Objects.equals(getPatternSafely(checkRegexp),
217                     getPatternSafely(suppressElement.checkRegexp))
218                 && Objects.equals(getPatternSafely(messageRegexp),
219                     getPatternSafely(suppressElement.messageRegexp))
220                 && Objects.equals(moduleId, suppressElement.moduleId)
221                 && Objects.equals(linesCsv, suppressElement.linesCsv)
222                 && Objects.equals(columnsCsv, suppressElement.columnsCsv);
223     }
224 
225     /**
226      * Util method to get pattern String value from Pattern object safely, return null if
227      * pattern object is null.
228      *
229      * @param pattern pattern object
230      * @return value of pattern or null
231      */
232     @Nullable
233     private static String getPatternSafely(Pattern pattern) {
234         String result = null;
235         if (pattern != null) {
236             result = pattern.pattern();
237         }
238         return result;
239     }
240 
241 }