View Javadoc
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 }