001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.api; 021 022import java.text.MessageFormat; 023import java.util.Arrays; 024import java.util.Locale; 025import java.util.Objects; 026 027import com.puppycrawl.tools.checkstyle.LocalizedMessage; 028import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil; 029 030/** 031 * Represents a violation that can be localised. The translations come from 032 * message.properties files. The underlying implementation uses 033 * java.text.MessageFormat. 034 * 035 * @noinspection ClassWithTooManyConstructors 036 * @noinspectionreason ClassWithTooManyConstructors - immutable nature of class requires a 037 * bunch of constructors 038 */ 039public final class Violation 040 implements Comparable<Violation> { 041 042 /** The default severity level if one is not specified. */ 043 private static final SeverityLevel DEFAULT_SEVERITY = SeverityLevel.ERROR; 044 045 /** The line number. **/ 046 private final int lineNo; 047 /** The column number. **/ 048 private final int columnNo; 049 /** The column char index. **/ 050 private final int columnCharIndex; 051 /** The token type constant. See {@link TokenTypes}. **/ 052 private final int tokenType; 053 054 /** The severity level. **/ 055 private final SeverityLevel severityLevel; 056 057 /** The id of the module generating the violation. */ 058 private final String moduleId; 059 060 /** Key for the violation format. **/ 061 private final String key; 062 063 /** Arguments for MessageFormat. */ 064 private final Object[] args; 065 066 /** Name of the resource bundle to get violations from. **/ 067 private final String bundle; 068 069 /** Class of the source for this Violation. */ 070 private final Class<?> sourceClass; 071 072 /** A custom violation overriding the default violation from the bundle. */ 073 private final String customMessage; 074 075 /** 076 * Creates a new {@code Violation} instance. 077 * 078 * @param lineNo line number associated with the violation 079 * @param columnNo column number associated with the violation 080 * @param columnCharIndex column char index associated with the violation 081 * @param tokenType token type of the event associated with violation. See {@link TokenTypes} 082 * @param bundle resource bundle name 083 * @param key the key to locate the translation 084 * @param args arguments for the translation 085 * @param severityLevel severity level for the violation 086 * @param moduleId the id of the module the violation is associated with 087 * @param sourceClass the Class that is the source of the violation 088 * @param customMessage optional custom violation overriding the default 089 * @noinspection ConstructorWithTooManyParameters 090 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 091 * number of arguments 092 */ 093 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 094 public Violation(int lineNo, 095 int columnNo, 096 int columnCharIndex, 097 int tokenType, 098 String bundle, 099 String key, 100 Object[] args, 101 SeverityLevel severityLevel, 102 String moduleId, 103 Class<?> sourceClass, 104 String customMessage) { 105 this.lineNo = lineNo; 106 this.columnNo = columnNo; 107 this.columnCharIndex = columnCharIndex; 108 this.tokenType = tokenType; 109 this.key = key; 110 111 if (args == null) { 112 this.args = null; 113 } 114 else { 115 this.args = UnmodifiableCollectionUtil.copyOfArray(args, args.length); 116 } 117 this.bundle = bundle; 118 this.severityLevel = severityLevel; 119 this.moduleId = moduleId; 120 this.sourceClass = sourceClass; 121 this.customMessage = customMessage; 122 } 123 124 /** 125 * Creates a new {@code Violation} instance. 126 * 127 * @param lineNo line number associated with the violation 128 * @param columnNo column number associated with the violation 129 * @param tokenType token type of the event associated with violation. See {@link TokenTypes} 130 * @param bundle resource bundle name 131 * @param key the key to locate the translation 132 * @param args arguments for the translation 133 * @param severityLevel severity level for the violation 134 * @param moduleId the id of the module the violation is associated with 135 * @param sourceClass the Class that is the source of the violation 136 * @param customMessage optional custom violation overriding the default 137 * @noinspection ConstructorWithTooManyParameters 138 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 139 * number of arguments 140 */ 141 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 142 public Violation(int lineNo, 143 int columnNo, 144 int tokenType, 145 String bundle, 146 String key, 147 Object[] args, 148 SeverityLevel severityLevel, 149 String moduleId, 150 Class<?> sourceClass, 151 String customMessage) { 152 this(lineNo, columnNo, columnNo, tokenType, bundle, key, args, severityLevel, moduleId, 153 sourceClass, customMessage); 154 } 155 156 /** 157 * Creates a new {@code Violation} instance. 158 * 159 * @param lineNo line number associated with the violation 160 * @param columnNo column number associated with the violation 161 * @param bundle resource bundle name 162 * @param key the key to locate the translation 163 * @param args arguments for the translation 164 * @param severityLevel severity level for the violation 165 * @param moduleId the id of the module the violation is associated with 166 * @param sourceClass the Class that is the source of the violation 167 * @param customMessage optional custom violation overriding the default 168 * @noinspection ConstructorWithTooManyParameters 169 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 170 * number of arguments 171 */ 172 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 173 public Violation(int lineNo, 174 int columnNo, 175 String bundle, 176 String key, 177 Object[] args, 178 SeverityLevel severityLevel, 179 String moduleId, 180 Class<?> sourceClass, 181 String customMessage) { 182 this(lineNo, columnNo, 0, bundle, key, args, severityLevel, moduleId, sourceClass, 183 customMessage); 184 } 185 186 /** 187 * Creates a new {@code Violation} instance. 188 * 189 * @param lineNo line number associated with the violation 190 * @param columnNo column number associated with the violation 191 * @param bundle resource bundle name 192 * @param key the key to locate the translation 193 * @param args arguments for the translation 194 * @param moduleId the id of the module the violation is associated with 195 * @param sourceClass the Class that is the source of the violation 196 * @param customMessage optional custom violation overriding the default 197 * @noinspection ConstructorWithTooManyParameters 198 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 199 * number of arguments 200 */ 201 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 202 public Violation(int lineNo, 203 int columnNo, 204 String bundle, 205 String key, 206 Object[] args, 207 String moduleId, 208 Class<?> sourceClass, 209 String customMessage) { 210 this(lineNo, 211 columnNo, 212 bundle, 213 key, 214 args, 215 DEFAULT_SEVERITY, 216 moduleId, 217 sourceClass, 218 customMessage); 219 } 220 221 /** 222 * Creates a new {@code Violation} instance. 223 * 224 * @param lineNo line number associated with the violation 225 * @param bundle resource bundle name 226 * @param key the key to locate the translation 227 * @param args arguments for the translation 228 * @param severityLevel severity level for the violation 229 * @param moduleId the id of the module the violation is associated with 230 * @param sourceClass the source class for the violation 231 * @param customMessage optional custom violation overriding the default 232 * @noinspection ConstructorWithTooManyParameters 233 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 234 * number of arguments 235 */ 236 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 237 public Violation(int lineNo, 238 String bundle, 239 String key, 240 Object[] args, 241 SeverityLevel severityLevel, 242 String moduleId, 243 Class<?> sourceClass, 244 String customMessage) { 245 this(lineNo, 0, bundle, key, args, severityLevel, moduleId, 246 sourceClass, customMessage); 247 } 248 249 /** 250 * Creates a new {@code Violation} instance. The column number 251 * defaults to 0. 252 * 253 * @param lineNo line number associated with the violation 254 * @param bundle name of a resource bundle that contains audit event violations 255 * @param key the key to locate the translation 256 * @param args arguments for the translation 257 * @param moduleId the id of the module the violation is associated with 258 * @param sourceClass the name of the source for the violation 259 * @param customMessage optional custom violation overriding the default 260 */ 261 public Violation( 262 int lineNo, 263 String bundle, 264 String key, 265 Object[] args, 266 String moduleId, 267 Class<?> sourceClass, 268 String customMessage) { 269 this(lineNo, 0, bundle, key, args, DEFAULT_SEVERITY, moduleId, 270 sourceClass, customMessage); 271 } 272 273 /** 274 * Gets the line number. 275 * 276 * @return the line number 277 */ 278 public int getLineNo() { 279 return lineNo; 280 } 281 282 /** 283 * Gets the column number. 284 * 285 * @return the column number 286 */ 287 public int getColumnNo() { 288 return columnNo; 289 } 290 291 /** 292 * Gets the column char index. 293 * 294 * @return the column char index 295 */ 296 public int getColumnCharIndex() { 297 return columnCharIndex; 298 } 299 300 /** 301 * Gets the token type. 302 * 303 * @return the token type 304 */ 305 public int getTokenType() { 306 return tokenType; 307 } 308 309 /** 310 * Gets the severity level. 311 * 312 * @return the severity level 313 */ 314 public SeverityLevel getSeverityLevel() { 315 return severityLevel; 316 } 317 318 /** 319 * Returns id of module. 320 * 321 * @return the module identifier. 322 */ 323 public String getModuleId() { 324 return moduleId; 325 } 326 327 /** 328 * Returns the violation key to locate the translation, can also be used 329 * in IDE plugins to map audit event violations to corrective actions. 330 * 331 * @return the violation key 332 */ 333 public String getKey() { 334 return key; 335 } 336 337 /** 338 * Gets the name of the source for this Violation. 339 * 340 * @return the name of the source for this Violation 341 */ 342 public String getSourceName() { 343 return sourceClass.getName(); 344 } 345 346 /** 347 * Indicates whether some other object is "equal to" this one. 348 * Suppression on enumeration is needed so code stays consistent. 349 * 350 * @noinspection EqualsCalledOnEnumConstant 351 * @noinspectionreason EqualsCalledOnEnumConstant - enumeration is needed to keep 352 * code consistent 353 */ 354 // -@cs[CyclomaticComplexity] equals - a lot of fields to check. 355 @Override 356 public boolean equals(Object object) { 357 if (this == object) { 358 return true; 359 } 360 if (object == null || getClass() != object.getClass()) { 361 return false; 362 } 363 final Violation violation = (Violation) object; 364 return Objects.equals(lineNo, violation.lineNo) 365 && Objects.equals(columnNo, violation.columnNo) 366 && Objects.equals(columnCharIndex, violation.columnCharIndex) 367 && Objects.equals(tokenType, violation.tokenType) 368 && Objects.equals(severityLevel, violation.severityLevel) 369 && Objects.equals(moduleId, violation.moduleId) 370 && Objects.equals(key, violation.key) 371 && Objects.equals(bundle, violation.bundle) 372 && Objects.equals(sourceClass, violation.sourceClass) 373 && Objects.equals(customMessage, violation.customMessage) 374 && Arrays.equals(args, violation.args); 375 } 376 377 @Override 378 public int hashCode() { 379 return Objects.hash(lineNo, columnNo, columnCharIndex, tokenType, severityLevel, moduleId, 380 key, bundle, sourceClass, customMessage, Arrays.hashCode(args)); 381 } 382 383 //////////////////////////////////////////////////////////////////////////// 384 // Interface Comparable methods 385 //////////////////////////////////////////////////////////////////////////// 386 387 @Override 388 public int compareTo(Violation other) { 389 final int result; 390 391 if (lineNo == other.lineNo) { 392 if (columnNo == other.columnNo) { 393 if (Objects.equals(moduleId, other.moduleId)) { 394 if (Objects.equals(sourceClass, other.sourceClass)) { 395 result = getViolation().compareTo(other.getViolation()); 396 } 397 else if (sourceClass == null) { 398 result = -1; 399 } 400 else if (other.sourceClass == null) { 401 result = 1; 402 } 403 else { 404 result = sourceClass.getName().compareTo(other.sourceClass.getName()); 405 } 406 } 407 else if (moduleId == null) { 408 result = -1; 409 } 410 else if (other.moduleId == null) { 411 result = 1; 412 } 413 else { 414 result = moduleId.compareTo(other.moduleId); 415 } 416 } 417 else { 418 result = Integer.compare(columnNo, other.columnNo); 419 } 420 } 421 else { 422 result = Integer.compare(lineNo, other.lineNo); 423 } 424 return result; 425 } 426 427 /** 428 * Gets the translated violation. 429 * 430 * @return the translated violation 431 */ 432 public String getViolation() { 433 final String violation; 434 435 if (customMessage != null) { 436 violation = new MessageFormat(customMessage, Locale.ROOT).format(args); 437 } 438 else { 439 violation = new LocalizedMessage(bundle, sourceClass, key, args).getMessage(); 440 } 441 442 return violation; 443 } 444 445}