1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 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 com.puppycrawl.tools.checkstyle.LocalizedMessage; 28 import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil; 29 30 /** 31 * Represents a violation that can be localised. The translations come from 32 * message.properties files. The underlying implementation uses 33 * java.text.MessageFormat. 34 * 35 * @noinspection ClassWithTooManyConstructors 36 * @noinspectionreason ClassWithTooManyConstructors - immutable nature of class requires a 37 * bunch of constructors 38 */ 39 public final class Violation 40 implements Comparable<Violation> { 41 42 /** The default severity level if one is not specified. */ 43 private static final SeverityLevel DEFAULT_SEVERITY = SeverityLevel.ERROR; 44 45 /** The line number. **/ 46 private final int lineNo; 47 /** The column number. **/ 48 private final int columnNo; 49 /** The column char index. **/ 50 private final int columnCharIndex; 51 /** The token type constant. See {@link TokenTypes}. **/ 52 private final int tokenType; 53 54 /** The severity level. **/ 55 private final SeverityLevel severityLevel; 56 57 /** The id of the module generating the violation. */ 58 private final String moduleId; 59 60 /** Key for the violation format. **/ 61 private final String key; 62 63 /** Arguments for MessageFormat. */ 64 private final Object[] args; 65 66 /** Name of the resource bundle to get violations from. **/ 67 private final String bundle; 68 69 /** Class of the source for this Violation. */ 70 private final Class<?> sourceClass; 71 72 /** A custom violation overriding the default violation from the bundle. */ 73 private final String customMessage; 74 75 /** 76 * Creates a new {@code Violation} instance. 77 * 78 * @param lineNo line number associated with the violation 79 * @param columnNo column number associated with the violation 80 * @param columnCharIndex column char index associated with the violation 81 * @param tokenType token type of the event associated with violation. See {@link TokenTypes} 82 * @param bundle resource bundle name 83 * @param key the key to locate the translation 84 * @param args arguments for the translation 85 * @param severityLevel severity level for the violation 86 * @param moduleId the id of the module the violation is associated with 87 * @param sourceClass the Class that is the source of the violation 88 * @param customMessage optional custom violation overriding the default 89 * @noinspection ConstructorWithTooManyParameters 90 * @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large 91 * number of arguments 92 */ 93 // -@cs[ParameterNumber] Class is immutable, we need that amount of arguments. 94 public Violation(int lineNo, 95 int columnNo, 96 int columnCharIndex, 97 int tokenType, 98 String bundle, 99 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 }