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