001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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 javax.annotation.Nullable; 026 027import com.puppycrawl.tools.checkstyle.api.AuditEvent; 028import com.puppycrawl.tools.checkstyle.api.Filter; 029 030/** 031 * This filter element is immutable and processes {@link AuditEvent} 032 * objects based on the criteria of file, check, module id, line, and 033 * column. It rejects an AuditEvent if the following match: 034 * <ul> 035 * <li>the event's file name; and</li> 036 * <li>the check name or the module identifier; and</li> 037 * <li>(optionally) the event's line is in the filter's line CSV; and</li> 038 * <li>(optionally) the check's columns is in the filter's column CSV.</li> 039 * </ul> 040 * 041 */ 042public class SuppressFilterElement 043 implements Filter { 044 045 /** The regexp to match file names against. */ 046 private final Pattern fileRegexp; 047 048 /** The regexp to match check names against. */ 049 private final Pattern checkRegexp; 050 051 /** The regexp to match message names against. */ 052 private final Pattern messageRegexp; 053 054 /** Module id filter. */ 055 private final String moduleId; 056 057 /** Line number filter. */ 058 private final CsvFilterElement lineFilter; 059 060 /** CSV for line number filter. */ 061 private final String linesCsv; 062 063 /** Column number filter. */ 064 private final CsvFilterElement columnFilter; 065 066 /** CSV for column number filter. */ 067 private final String columnsCsv; 068 069 /** 070 * Creates a {@code SuppressFilterElement} instance. 071 * 072 * @param files regular expression for filtered file names 073 * @param checks regular expression for filtered check classes 074 * @param message regular expression for messages. 075 * @param moduleId the module id 076 * @param lines CSV for lines 077 * @param columns CSV for columns 078 */ 079 public SuppressFilterElement(Pattern files, Pattern checks, Pattern message, String moduleId, 080 String lines, String columns) { 081 fileRegexp = files; 082 checkRegexp = checks; 083 messageRegexp = message; 084 this.moduleId = moduleId; 085 if (lines == null) { 086 linesCsv = null; 087 lineFilter = null; 088 } 089 else { 090 linesCsv = lines; 091 lineFilter = new CsvFilterElement(lines); 092 } 093 if (columns == null) { 094 columnsCsv = null; 095 columnFilter = null; 096 } 097 else { 098 columnsCsv = columns; 099 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}