View Javadoc
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 }