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