Violation.java
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2024 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///////////////////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.api;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import com.puppycrawl.tools.checkstyle.LocalizedMessage;
import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
/**
* Represents a violation that can be localised. The translations come from
* message.properties files. The underlying implementation uses
* java.text.MessageFormat.
*
* @noinspection ClassWithTooManyConstructors
* @noinspectionreason ClassWithTooManyConstructors - immutable nature of class requires a
* bunch of constructors
*/
public final class Violation
implements Comparable<Violation> {
/** The default severity level if one is not specified. */
private static final SeverityLevel DEFAULT_SEVERITY = SeverityLevel.ERROR;
/** The line number. **/
private final int lineNo;
/** The column number. **/
private final int columnNo;
/** The column char index. **/
private final int columnCharIndex;
/** The token type constant. See {@link TokenTypes}. **/
private final int tokenType;
/** The severity level. **/
private final SeverityLevel severityLevel;
/** The id of the module generating the violation. */
private final String moduleId;
/** Key for the violation format. **/
private final String key;
/** Arguments for MessageFormat. */
private final Object[] args;
/** Name of the resource bundle to get violations from. **/
private final String bundle;
/** Class of the source for this Violation. */
private final Class<?> sourceClass;
/** A custom violation overriding the default violation from the bundle. */
private final String customMessage;
/**
* Creates a new {@code Violation} instance.
*
* @param lineNo line number associated with the violation
* @param columnNo column number associated with the violation
* @param columnCharIndex column char index associated with the violation
* @param tokenType token type of the event associated with violation. See {@link TokenTypes}
* @param bundle resource bundle name
* @param key the key to locate the translation
* @param args arguments for the translation
* @param severityLevel severity level for the violation
* @param moduleId the id of the module the violation is associated with
* @param sourceClass the Class that is the source of the violation
* @param customMessage optional custom violation overriding the default
* @noinspection ConstructorWithTooManyParameters
* @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large
* number of arguments
*/
// -@cs[ParameterNumber] Class is immutable, we need that amount of arguments.
public Violation(int lineNo,
int columnNo,
int columnCharIndex,
int tokenType,
String bundle,
String key,
Object[] args,
SeverityLevel severityLevel,
String moduleId,
Class<?> sourceClass,
String customMessage) {
this.lineNo = lineNo;
this.columnNo = columnNo;
this.columnCharIndex = columnCharIndex;
this.tokenType = tokenType;
this.key = key;
if (args == null) {
this.args = null;
}
else {
this.args = UnmodifiableCollectionUtil.copyOfArray(args, args.length);
}
this.bundle = bundle;
this.severityLevel = severityLevel;
this.moduleId = moduleId;
this.sourceClass = sourceClass;
this.customMessage = customMessage;
}
/**
* Creates a new {@code Violation} instance.
*
* @param lineNo line number associated with the violation
* @param columnNo column number associated with the violation
* @param tokenType token type of the event associated with violation. See {@link TokenTypes}
* @param bundle resource bundle name
* @param key the key to locate the translation
* @param args arguments for the translation
* @param severityLevel severity level for the violation
* @param moduleId the id of the module the violation is associated with
* @param sourceClass the Class that is the source of the violation
* @param customMessage optional custom violation overriding the default
* @noinspection ConstructorWithTooManyParameters
* @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large
* number of arguments
*/
// -@cs[ParameterNumber] Class is immutable, we need that amount of arguments.
public Violation(int lineNo,
int columnNo,
int tokenType,
String bundle,
String key,
Object[] args,
SeverityLevel severityLevel,
String moduleId,
Class<?> sourceClass,
String customMessage) {
this(lineNo, columnNo, columnNo, tokenType, bundle, key, args, severityLevel, moduleId,
sourceClass, customMessage);
}
/**
* Creates a new {@code Violation} instance.
*
* @param lineNo line number associated with the violation
* @param columnNo column number associated with the violation
* @param bundle resource bundle name
* @param key the key to locate the translation
* @param args arguments for the translation
* @param severityLevel severity level for the violation
* @param moduleId the id of the module the violation is associated with
* @param sourceClass the Class that is the source of the violation
* @param customMessage optional custom violation overriding the default
* @noinspection ConstructorWithTooManyParameters
* @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large
* number of arguments
*/
// -@cs[ParameterNumber] Class is immutable, we need that amount of arguments.
public Violation(int lineNo,
int columnNo,
String bundle,
String key,
Object[] args,
SeverityLevel severityLevel,
String moduleId,
Class<?> sourceClass,
String customMessage) {
this(lineNo, columnNo, 0, bundle, key, args, severityLevel, moduleId, sourceClass,
customMessage);
}
/**
* Creates a new {@code Violation} instance.
*
* @param lineNo line number associated with the violation
* @param columnNo column number associated with the violation
* @param bundle resource bundle name
* @param key the key to locate the translation
* @param args arguments for the translation
* @param moduleId the id of the module the violation is associated with
* @param sourceClass the Class that is the source of the violation
* @param customMessage optional custom violation overriding the default
* @noinspection ConstructorWithTooManyParameters
* @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large
* number of arguments
*/
// -@cs[ParameterNumber] Class is immutable, we need that amount of arguments.
public Violation(int lineNo,
int columnNo,
String bundle,
String key,
Object[] args,
String moduleId,
Class<?> sourceClass,
String customMessage) {
this(lineNo,
columnNo,
bundle,
key,
args,
DEFAULT_SEVERITY,
moduleId,
sourceClass,
customMessage);
}
/**
* Creates a new {@code Violation} instance.
*
* @param lineNo line number associated with the violation
* @param bundle resource bundle name
* @param key the key to locate the translation
* @param args arguments for the translation
* @param severityLevel severity level for the violation
* @param moduleId the id of the module the violation is associated with
* @param sourceClass the source class for the violation
* @param customMessage optional custom violation overriding the default
* @noinspection ConstructorWithTooManyParameters
* @noinspectionreason ConstructorWithTooManyParameters - immutable class requires a large
* number of arguments
*/
// -@cs[ParameterNumber] Class is immutable, we need that amount of arguments.
public Violation(int lineNo,
String bundle,
String key,
Object[] args,
SeverityLevel severityLevel,
String moduleId,
Class<?> sourceClass,
String customMessage) {
this(lineNo, 0, bundle, key, args, severityLevel, moduleId,
sourceClass, customMessage);
}
/**
* Creates a new {@code Violation} instance. The column number
* defaults to 0.
*
* @param lineNo line number associated with the violation
* @param bundle name of a resource bundle that contains audit event violations
* @param key the key to locate the translation
* @param args arguments for the translation
* @param moduleId the id of the module the violation is associated with
* @param sourceClass the name of the source for the violation
* @param customMessage optional custom violation overriding the default
*/
public Violation(
int lineNo,
String bundle,
String key,
Object[] args,
String moduleId,
Class<?> sourceClass,
String customMessage) {
this(lineNo, 0, bundle, key, args, DEFAULT_SEVERITY, moduleId,
sourceClass, customMessage);
}
/**
* Gets the line number.
*
* @return the line number
*/
public int getLineNo() {
return lineNo;
}
/**
* Gets the column number.
*
* @return the column number
*/
public int getColumnNo() {
return columnNo;
}
/**
* Gets the column char index.
*
* @return the column char index
*/
public int getColumnCharIndex() {
return columnCharIndex;
}
/**
* Gets the token type.
*
* @return the token type
*/
public int getTokenType() {
return tokenType;
}
/**
* Gets the severity level.
*
* @return the severity level
*/
public SeverityLevel getSeverityLevel() {
return severityLevel;
}
/**
* Returns id of module.
*
* @return the module identifier.
*/
public String getModuleId() {
return moduleId;
}
/**
* Returns the violation key to locate the translation, can also be used
* in IDE plugins to map audit event violations to corrective actions.
*
* @return the violation key
*/
public String getKey() {
return key;
}
/**
* Gets the name of the source for this Violation.
*
* @return the name of the source for this Violation
*/
public String getSourceName() {
return sourceClass.getName();
}
/**
* Indicates whether some other object is "equal to" this one.
* Suppression on enumeration is needed so code stays consistent.
*
* @noinspection EqualsCalledOnEnumConstant
* @noinspectionreason EqualsCalledOnEnumConstant - enumeration is needed to keep
* code consistent
*/
// -@cs[CyclomaticComplexity] equals - a lot of fields to check.
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
final Violation violation = (Violation) object;
return Objects.equals(lineNo, violation.lineNo)
&& Objects.equals(columnNo, violation.columnNo)
&& Objects.equals(columnCharIndex, violation.columnCharIndex)
&& Objects.equals(tokenType, violation.tokenType)
&& Objects.equals(severityLevel, violation.severityLevel)
&& Objects.equals(moduleId, violation.moduleId)
&& Objects.equals(key, violation.key)
&& Objects.equals(bundle, violation.bundle)
&& Objects.equals(sourceClass, violation.sourceClass)
&& Objects.equals(customMessage, violation.customMessage)
&& Arrays.equals(args, violation.args);
}
@Override
public int hashCode() {
return Objects.hash(lineNo, columnNo, columnCharIndex, tokenType, severityLevel, moduleId,
key, bundle, sourceClass, customMessage, Arrays.hashCode(args));
}
////////////////////////////////////////////////////////////////////////////
// Interface Comparable methods
////////////////////////////////////////////////////////////////////////////
@Override
public int compareTo(Violation other) {
final int result;
if (lineNo == other.lineNo) {
if (columnNo == other.columnNo) {
if (Objects.equals(moduleId, other.moduleId)) {
if (Objects.equals(sourceClass, other.sourceClass)) {
result = getViolation().compareTo(other.getViolation());
}
else if (sourceClass == null) {
result = -1;
}
else if (other.sourceClass == null) {
result = 1;
}
else {
result = sourceClass.getName().compareTo(other.sourceClass.getName());
}
}
else if (moduleId == null) {
result = -1;
}
else if (other.moduleId == null) {
result = 1;
}
else {
result = moduleId.compareTo(other.moduleId);
}
}
else {
result = Integer.compare(columnNo, other.columnNo);
}
}
else {
result = Integer.compare(lineNo, other.lineNo);
}
return result;
}
/**
* Gets the translated violation.
*
* @return the translated violation
*/
public String getViolation() {
final String violation;
if (customMessage != null) {
violation = new MessageFormat(customMessage, Locale.ROOT).format(args);
}
else {
violation = new LocalizedMessage(bundle, sourceClass, key, args).getMessage();
}
return violation;
}
}