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.api; 21 22 import java.text.MessageFormat; 23 import java.util.Arrays; 24 import java.util.Locale; 25 import java.util.Objects; 26 27 import javax.annotation.Nullable; 28 29 import com.puppycrawl.tools.checkstyle.LocalizedMessage; 30 import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil; 31 32 /** 33 * Represents a violation that can be localised. The translations come from 34 * message.properties files. The underlying implementation uses 35 * java.text.MessageFormat. 36 * 37 * @noinspection ClassWithTooManyConstructors 38 * @noinspectionreason ClassWithTooManyConstructors - immutable nature of class requires a 39 * bunch of constructors 40 */ 41 public final class Violation 42 implements Comparable<Violation> { 43 44 /** The default severity level if one is not specified. */ 45 private static final SeverityLevel DEFAULT_SEVERITY = SeverityLevel.ERROR; 46 47 /** The line number. **/ 48 private final int lineNo; 49 /** The column number. **/ 50 private final int columnNo; 51 /** The column char index. **/ 52 private final int columnCharIndex; 53 /** The token type constant. See {@link TokenTypes}. **/ 54 private final int tokenType; 55 56 /** The severity level. **/ 57 private final SeverityLevel severityLevel; 58 59 /** The id of the module generating the violation. */ 60 private final String moduleId; 61 62 /** Key for the violation format. **/ 63 private final String key; 64 65 /** Arguments for MessageFormat. */ 66 private final Object[] args; 67 68 /** Name of the resource bundle to get violations from. **/ 69 private final String bundle; 70 71 /** Class of the source for this Violation. */ 72 private final Class<?> sourceClass; 73 74 /** A custom violation overriding the default violation from the bundle. */ 75 @Nullable 76 private final String customMessage; 77 78 /** 79 * Creates a new {@code Violation} instance. 80 * 81 * @param lineNo line number associated with the violation 82 * @param columnNo column number associated with the violation 83 * @param columnCharIndex column char index associated with the violation 84 * @param tokenType token type of the event associated with violation. See {@link TokenTypes} 85 * @param bundle resource bundle name 86 * @param key the key to locate the translation 87 * @param args arguments for the translation 88 * @param severityLevel severity level for the violation 89 * @param moduleId the id of the module the violation is associated with 90 * @param sourceClass the Class that is the source of the violation 91 * @param customMessage optional custom violation overriding the default 92 * @noinspection ConstructorWithTooManyParameters 93 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 94 * number of arguments 95 */ 96 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 97 public Violation(int lineNo, 98 int columnNo, 99 int columnCharIndex, 100 int tokenType, 101 String bundle, 102 String key, 103 Object[] args, 104 SeverityLevel severityLevel, 105 String moduleId, 106 Class<?> sourceClass, 107 @Nullable String customMessage) { 108 this.lineNo = lineNo; 109 this.columnNo = columnNo; 110 this.columnCharIndex = columnCharIndex; 111 this.tokenType = tokenType; 112 this.key = key; 113 114 if (args == null) { 115 this.args = null; 116 } 117 else { 118 this.args = UnmodifiableCollectionUtil.copyOfArray(args, args.length); 119 } 120 this.bundle = bundle; 121 this.severityLevel = severityLevel; 122 this.moduleId = moduleId; 123 this.sourceClass = sourceClass; 124 this.customMessage = customMessage; 125 } 126 127 /** 128 * Creates a new {@code Violation} instance. 129 * 130 * @param lineNo line number associated with the violation 131 * @param columnNo column number associated with the violation 132 * @param tokenType token type of the event associated with violation. See {@link TokenTypes} 133 * @param bundle resource bundle name 134 * @param key the key to locate the translation 135 * @param args arguments for the translation 136 * @param severityLevel severity level for the violation 137 * @param moduleId the id of the module the violation is associated with 138 * @param sourceClass the Class that is the source of the violation 139 * @param customMessage optional custom violation overriding the default 140 * @noinspection ConstructorWithTooManyParameters 141 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 142 * number of arguments 143 */ 144 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 145 public Violation(int lineNo, 146 int columnNo, 147 int tokenType, 148 String bundle, 149 String key, 150 Object[] args, 151 SeverityLevel severityLevel, 152 String moduleId, 153 Class<?> sourceClass, 154 @Nullable String customMessage) { 155 this(lineNo, columnNo, columnNo, tokenType, bundle, key, args, severityLevel, moduleId, 156 sourceClass, customMessage); 157 } 158 159 /** 160 * Creates a new {@code Violation} instance. 161 * 162 * @param lineNo line number associated with the violation 163 * @param columnNo column number associated with the violation 164 * @param bundle resource bundle name 165 * @param key the key to locate the translation 166 * @param args arguments for the translation 167 * @param severityLevel severity level for the violation 168 * @param moduleId the id of the module the violation is associated with 169 * @param sourceClass the Class that is the source of the violation 170 * @param customMessage optional custom violation overriding the default 171 * @noinspection ConstructorWithTooManyParameters 172 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 173 * number of arguments 174 */ 175 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 176 public Violation(int lineNo, 177 int columnNo, 178 String bundle, 179 String key, 180 Object[] args, 181 SeverityLevel severityLevel, 182 String moduleId, 183 Class<?> sourceClass, 184 @Nullable String customMessage) { 185 this(lineNo, columnNo, 0, bundle, key, args, severityLevel, moduleId, sourceClass, 186 customMessage); 187 } 188 189 /** 190 * Creates a new {@code Violation} instance. 191 * 192 * @param lineNo line number associated with the violation 193 * @param columnNo column number associated with the violation 194 * @param bundle resource bundle name 195 * @param key the key to locate the translation 196 * @param args arguments for the translation 197 * @param moduleId the id of the module the violation is associated with 198 * @param sourceClass the Class that is the source of the violation 199 * @param customMessage optional custom violation overriding the default 200 * @noinspection ConstructorWithTooManyParameters 201 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 202 * number of arguments 203 */ 204 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 205 public Violation(int lineNo, 206 int columnNo, 207 String bundle, 208 String key, 209 Object[] args, 210 String moduleId, 211 Class<?> sourceClass, 212 @Nullable String customMessage) { 213 this(lineNo, 214 columnNo, 215 bundle, 216 key, 217 args, 218 DEFAULT_SEVERITY, 219 moduleId, 220 sourceClass, 221 customMessage); 222 } 223 224 /** 225 * Creates a new {@code Violation} instance. 226 * 227 * @param lineNo line number associated with the violation 228 * @param bundle resource bundle name 229 * @param key the key to locate the translation 230 * @param args arguments for the translation 231 * @param severityLevel severity level for the violation 232 * @param moduleId the id of the module the violation is associated with 233 * @param sourceClass the source class for the violation 234 * @param customMessage optional custom violation overriding the default 235 * @noinspection ConstructorWithTooManyParameters 236 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 237 * number of arguments 238 */ 239 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 240 public Violation(int lineNo, 241 String bundle, 242 String key, 243 Object[] args, 244 SeverityLevel severityLevel, 245 String moduleId, 246 Class<?> sourceClass, 247 @Nullable String customMessage) { 248 this(lineNo, 0, bundle, key, args, severityLevel, moduleId, 249 sourceClass, customMessage); 250 } 251 252 /** 253 * Creates a new {@code Violation} instance. The column number 254 * defaults to 0. 255 * 256 * @param lineNo line number associated with the violation 257 * @param bundle name of a resource bundle that contains audit event violations 258 * @param key the key to locate the translation 259 * @param args arguments for the translation 260 * @param moduleId the id of the module the violation is associated with 261 * @param sourceClass the name of the source for the violation 262 * @param customMessage optional custom violation overriding the default 263 */ 264 public Violation( 265 int lineNo, 266 String bundle, 267 String key, 268 Object[] args, 269 String moduleId, 270 Class<?> sourceClass, 271 @Nullable String customMessage) { 272 this(lineNo, 0, bundle, key, args, DEFAULT_SEVERITY, moduleId, 273 sourceClass, customMessage); 274 } 275 276 /** 277 * Gets the line number. 278 * 279 * @return the line number 280 */ 281 public int getLineNo() { 282 return lineNo; 283 } 284 285 /** 286 * Gets the column number. 287 * 288 * @return the column number 289 */ 290 public int getColumnNo() { 291 return columnNo; 292 } 293 294 /** 295 * Gets the column char index. 296 * 297 * @return the column char index 298 */ 299 public int getColumnCharIndex() { 300 return columnCharIndex; 301 } 302 303 /** 304 * Gets the token type. 305 * 306 * @return the token type 307 */ 308 public int getTokenType() { 309 return tokenType; 310 } 311 312 /** 313 * Gets the severity level. 314 * 315 * @return the severity level 316 */ 317 public SeverityLevel getSeverityLevel() { 318 return severityLevel; 319 } 320 321 /** 322 * Returns id of module. 323 * 324 * @return the module identifier. 325 */ 326 public String getModuleId() { 327 return moduleId; 328 } 329 330 /** 331 * Returns the violation key to locate the translation, can also be used 332 * in IDE plugins to map audit event violations to corrective actions. 333 * 334 * @return the violation key 335 */ 336 public String getKey() { 337 return key; 338 } 339 340 /** 341 * Gets the name of the source for this Violation. 342 * 343 * @return the name of the source for this Violation 344 */ 345 public String getSourceName() { 346 return sourceClass.getName(); 347 } 348 349 /** 350 * Indicates whether some other object is "equal to" this one. 351 * Suppression on enumeration is needed so code stays consistent. 352 * 353 * @noinspection EqualsCalledOnEnumConstant 354 * @noinspectionreason EqualsCalledOnEnumConstant - enumeration is needed to keep 355 * code consistent 356 */ 357 // -@cs[CyclomaticComplexity] equals - a lot of fields to check. 358 @Override 359 public boolean equals(Object object) { 360 if (this == object) { 361 return true; 362 } 363 if (object == null || getClass() != object.getClass()) { 364 return false; 365 } 366 final Violation violation = (Violation) object; 367 return Objects.equals(lineNo, violation.lineNo) 368 && Objects.equals(columnNo, violation.columnNo) 369 && Objects.equals(columnCharIndex, violation.columnCharIndex) 370 && Objects.equals(tokenType, violation.tokenType) 371 && Objects.equals(severityLevel, violation.severityLevel) 372 && Objects.equals(moduleId, violation.moduleId) 373 && Objects.equals(key, violation.key) 374 && Objects.equals(bundle, violation.bundle) 375 && Objects.equals(sourceClass, violation.sourceClass) 376 && Objects.equals(customMessage, violation.customMessage) 377 && Arrays.equals(args, violation.args); 378 } 379 380 @Override 381 public int hashCode() { 382 return Objects.hash(lineNo, columnNo, columnCharIndex, tokenType, severityLevel, moduleId, 383 key, bundle, sourceClass, customMessage, Arrays.hashCode(args)); 384 } 385 386 //////////////////////////////////////////////////////////////////////////// 387 // Interface Comparable methods 388 //////////////////////////////////////////////////////////////////////////// 389 390 @Override 391 public int compareTo(Violation other) { 392 final int result; 393 394 if (lineNo == other.lineNo) { 395 if (columnNo == other.columnNo) { 396 if (Objects.equals(moduleId, other.moduleId)) { 397 if (Objects.equals(sourceClass, other.sourceClass)) { 398 result = getViolation().compareTo(other.getViolation()); 399 } 400 else if (sourceClass == null) { 401 result = -1; 402 } 403 else if (other.sourceClass == null) { 404 result = 1; 405 } 406 else { 407 result = sourceClass.getName().compareTo(other.sourceClass.getName()); 408 } 409 } 410 else if (moduleId == null) { 411 result = -1; 412 } 413 else if (other.moduleId == null) { 414 result = 1; 415 } 416 else { 417 result = moduleId.compareTo(other.moduleId); 418 } 419 } 420 else { 421 result = Integer.compare(columnNo, other.columnNo); 422 } 423 } 424 else { 425 result = Integer.compare(lineNo, other.lineNo); 426 } 427 return result; 428 } 429 430 /** 431 * Gets the translated violation. 432 * 433 * @return the translated violation 434 */ 435 public String getViolation() { 436 final String violation; 437 438 if (customMessage != null) { 439 violation = new MessageFormat(customMessage, Locale.ROOT).format(args); 440 } 441 else { 442 violation = new LocalizedMessage(bundle, sourceClass, key, args).getMessage(); 443 } 444 445 return violation; 446 } 447 448 }