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;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  
24  import java.io.ByteArrayInputStream;
25  import java.io.ByteArrayOutputStream;
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.InputStreamReader;
29  import java.io.LineNumberReader;
30  import java.nio.charset.StandardCharsets;
31  import java.text.MessageFormat;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collections;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Locale;
38  import java.util.Map;
39  import java.util.ResourceBundle;
40  import java.util.stream.Collectors;
41  
42  import com.google.common.collect.ImmutableMap;
43  import com.google.common.collect.Maps;
44  import com.puppycrawl.tools.checkstyle.LocalizedMessage.Utf8Control;
45  import com.puppycrawl.tools.checkstyle.api.Configuration;
46  import com.puppycrawl.tools.checkstyle.bdd.InlineConfigParser;
47  import com.puppycrawl.tools.checkstyle.bdd.TestInputConfiguration;
48  import com.puppycrawl.tools.checkstyle.bdd.TestInputViolation;
49  import com.puppycrawl.tools.checkstyle.internal.utils.BriefUtLogger;
50  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
51  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
52  import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtil;
53  
54  public abstract class AbstractModuleTestSupport extends AbstractPathTestSupport {
55  
56      protected static final String ROOT_MODULE_NAME = Checker.class.getSimpleName();
57  
58      private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
59  
60      /**
61       * Returns log stream.
62       *
63       * @return stream with log
64       */
65      protected final ByteArrayOutputStream getStream() {
66          return stream;
67      }
68  
69      /**
70       * Returns test logger.
71       *
72       * @return logger for tests
73       */
74      protected final BriefUtLogger getBriefUtLogger() {
75          return new BriefUtLogger(stream);
76      }
77  
78      /**
79       * Creates a default module configuration {@link DefaultConfiguration} for a given object
80       * of type {@link Class}.
81       *
82       * @param clazz a {@link Class} type object.
83       * @return default module configuration for the given {@link Class} instance.
84       */
85      protected static DefaultConfiguration createModuleConfig(Class<?> clazz) {
86          return new DefaultConfiguration(clazz.getName());
87      }
88  
89      /**
90       * Creates {@link Checker} instance based on the given {@link Configuration} instance.
91       *
92       * @param moduleConfig {@link Configuration} instance.
93       * @return {@link Checker} instance based on the given {@link Configuration} instance.
94       * @throws Exception if an exception occurs during checker configuration.
95       */
96      protected final Checker createChecker(Configuration moduleConfig)
97              throws Exception {
98          final String moduleName = moduleConfig.getName();
99          final Checker checker = new Checker();
100         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
101 
102         if (ROOT_MODULE_NAME.equals(moduleName)) {
103             checker.configure(moduleConfig);
104         }
105         else {
106             configureChecker(checker, moduleConfig);
107         }
108 
109         checker.addListener(getBriefUtLogger());
110         return checker;
111     }
112 
113     /**
114      * Configures the {@code checker} instance with {@code moduleConfig}.
115      *
116      * @param checker {@link Checker} instance.
117      * @param moduleConfig {@link Configuration} instance.
118      * @throws Exception if an exception occurs during configuration.
119      */
120     protected void configureChecker(Checker checker, Configuration moduleConfig) throws Exception {
121         final Class<?> moduleClass = Class.forName(moduleConfig.getName());
122 
123         if (ModuleReflectionUtil.isCheckstyleTreeWalkerCheck(moduleClass)
124                 || ModuleReflectionUtil.isTreeWalkerFilterModule(moduleClass)) {
125             final Configuration config = createTreeWalkerConfig(moduleConfig);
126             checker.configure(config);
127         }
128         else {
129             final Configuration config = createRootConfig(moduleConfig);
130             checker.configure(config);
131         }
132     }
133 
134     /**
135      * Creates {@link DefaultConfiguration} for the {@link TreeWalker}
136      * based on the given {@link Configuration} instance.
137      *
138      * @param config {@link Configuration} instance.
139      * @return {@link DefaultConfiguration} for the {@link TreeWalker}
140      *     based on the given {@link Configuration} instance.
141      */
142     protected static DefaultConfiguration createTreeWalkerConfig(Configuration config) {
143         final DefaultConfiguration rootConfig =
144                 new DefaultConfiguration(ROOT_MODULE_NAME);
145         final DefaultConfiguration twConf = createModuleConfig(TreeWalker.class);
146         // make sure that the tests always run with this charset
147         rootConfig.addProperty("charset", StandardCharsets.UTF_8.name());
148         rootConfig.addChild(twConf);
149         twConf.addChild(config);
150         return rootConfig;
151     }
152 
153     /**
154      * Creates {@link DefaultConfiguration} for the given {@link Configuration} instance.
155      *
156      * @param config {@link Configuration} instance.
157      * @return {@link DefaultConfiguration} for the given {@link Configuration} instance.
158      */
159     protected static DefaultConfiguration createRootConfig(Configuration config) {
160         final DefaultConfiguration rootConfig = new DefaultConfiguration(ROOT_MODULE_NAME);
161         if (config != null) {
162             rootConfig.addChild(config);
163         }
164         return rootConfig;
165     }
166 
167     /**
168      * Returns canonical path for the file with the given file name.
169      * The path is formed base on the non-compilable resources location.
170      *
171      * @param filename file name.
172      * @return canonical path for the file with the given file name.
173      * @throws IOException if I/O exception occurs while forming the path.
174      */
175     protected final String getNonCompilablePath(String filename) throws IOException {
176         return new File("src/" + getResourceLocation()
177                 + "/resources-noncompilable/" + getPackageLocation() + "/"
178                 + filename).getCanonicalPath();
179     }
180 
181     /**
182      * Returns URI-representation of the path for the given file name.
183      * The path is formed base on the root location.
184      *
185      * @param filename file name.
186      * @return URI-representation of the path for the file with the given file name.
187      */
188     protected final String getUriString(String filename) {
189         return new File("src/test/resources/" + getPackageLocation() + "/" + filename).toURI()
190                 .toString();
191     }
192 
193     /**
194      * Performs verification of the file with the given file path using specified configuration
195      * and the array of expected messages. Also performs verification of the config with filters
196      * specified in the input file.
197      *
198      * @param filePath file path to verify.
199      * @param expectedUnfiltered an array of expected unfiltered config.
200      * @param expectedFiltered an array of expected config with filters.
201      * @throws Exception if exception occurs during verification process.
202      */
203     protected final void verifyFilterWithInlineConfigParser(String filePath,
204                                                             String[] expectedUnfiltered,
205                                                             String... expectedFiltered)
206             throws Exception {
207         final TestInputConfiguration testInputConfiguration =
208                 InlineConfigParser.parseWithFilteredViolations(filePath);
209         final DefaultConfiguration configWithoutFilters =
210                 testInputConfiguration.createConfigurationWithoutFilters();
211         final List<TestInputViolation> violationsWithoutFilters =
212                 new ArrayList<>(testInputConfiguration.getViolations());
213         violationsWithoutFilters.addAll(testInputConfiguration.getFilteredViolations());
214         Collections.sort(violationsWithoutFilters);
215         verifyViolations(configWithoutFilters, filePath, violationsWithoutFilters);
216         verify(configWithoutFilters, filePath, expectedUnfiltered);
217         final DefaultConfiguration configWithFilters =
218                 testInputConfiguration.createConfiguration();
219         verifyViolations(configWithFilters, filePath, testInputConfiguration.getViolations());
220         verify(configWithFilters, filePath, expectedFiltered);
221     }
222 
223     /**
224      * Performs verification of the file with given file path using configurations parsed from
225      * xml header of the file and the array expected messages. Also performs verification of
226      * the config specified in input file.
227      *
228      * @param filePath file path to verify
229      * @param expected an array of expected messages
230      * @throws Exception if exception occurs
231      */
232     protected final void verifyWithInlineXmlConfig(String filePath, String... expected)
233             throws Exception {
234         final TestInputConfiguration testInputConfiguration =
235                 InlineConfigParser.parseWithXmlHeader(filePath);
236         final Configuration xmlConfig =
237                 testInputConfiguration.getXmlConfiguration();
238         verifyViolations(xmlConfig, filePath, testInputConfiguration.getViolations());
239         verify(xmlConfig, filePath, expected);
240     }
241 
242     /**
243      * Performs verification of the file with the given file path using specified configuration
244      * and the array expected messages. Also performs verification of the config specified in
245      * input file.
246      *
247      * @param filePath file path to verify.
248      * @param expected an array of expected messages.
249      * @throws Exception if exception occurs during verification process.
250      */
251     protected final void verifyWithInlineConfigParser(String filePath, String... expected)
252             throws Exception {
253         final TestInputConfiguration testInputConfiguration =
254                 InlineConfigParser.parse(filePath);
255         final DefaultConfiguration parsedConfig =
256                 testInputConfiguration.createConfiguration();
257         final List<String> actualViolations = getActualViolationsForFile(parsedConfig, filePath);
258         verifyViolations(filePath, testInputConfiguration.getViolations(), actualViolations);
259         assertWithMessage("Violations for %s differ.", filePath)
260             .that(actualViolations)
261             .containsExactlyElementsIn(expected);
262     }
263 
264     /**
265      * Performs verification of two files with their given file paths using specified
266      * configuration of one file only. Also performs verification of the config specified
267      * in the input file. This method needs to be implemented when two given files need to be
268      * checked through a single check only.
269      *
270      * @param filePath1 file path of first file to verify
271      * @param filePath2 file path of second file to verify
272      * @param expected an array of expected messages
273      * @throws Exception if exception occurs during verification process
274      */
275     protected final void verifyWithInlineConfigParser(String filePath1,
276                                                       String filePath2,
277                                                       String... expected)
278             throws Exception {
279         final TestInputConfiguration testInputConfiguration1 =
280                 InlineConfigParser.parse(filePath1);
281         final DefaultConfiguration parsedConfig =
282                 testInputConfiguration1.createConfiguration();
283         final TestInputConfiguration testInputConfiguration2 =
284                 InlineConfigParser.parse(filePath2);
285         verifyViolations(parsedConfig, filePath1, testInputConfiguration1.getViolations());
286         verifyViolations(parsedConfig, filePath2, testInputConfiguration2.getViolations());
287         verify(createChecker(parsedConfig),
288                 new File[] {new File(filePath1), new File(filePath2)},
289                 filePath1,
290                 expected);
291     }
292 
293     /**
294      * Performs verification of two files with their given file paths.
295      * using specified configuration of one file only. Also performs
296      * verification of the config specified in the input file. This method
297      * needs to be implemented when two given files need to be
298      * checked through a single check only.
299      *
300      * @param filePath1 file path of first file to verify
301      * @param filePath2 file path of first file to verify
302      * @param expectedFromFile1 list of expected message
303      * @param expectedFromFile2 list of expected message
304      * @throws Exception if exception occurs during verification process
305      */
306     protected final void verifyWithInlineConfigParser(String filePath1,
307                                                       String filePath2,
308                                                       List<String> expectedFromFile1,
309                                                       List<String> expectedFromFile2)
310             throws Exception {
311         final TestInputConfiguration testInputConfiguration = InlineConfigParser.parse(filePath1);
312         final DefaultConfiguration parsedConfig = testInputConfiguration.createConfiguration();
313         final TestInputConfiguration testInputConfiguration2 = InlineConfigParser.parse(filePath2);
314         final DefaultConfiguration parsedConfig2 = testInputConfiguration.createConfiguration();
315         final File[] inputs = {new File(filePath1), new File(filePath2)};
316         verifyViolations(parsedConfig, filePath1, testInputConfiguration.getViolations());
317         verifyViolations(parsedConfig2, filePath2, testInputConfiguration2.getViolations());
318         verify(createChecker(parsedConfig), inputs, ImmutableMap.of(
319             filePath1, expectedFromFile1,
320             filePath2, expectedFromFile2));
321     }
322 
323     /**
324      * Performs verification of the file with the given file path using specified configuration
325      * and the array expected messages. Also performs verification of the config specified in
326      * input file
327      *
328      * @param filePath file path to verify.
329      * @param expected an array of expected messages.
330      * @throws Exception if exception occurs during verification process.
331      */
332     protected void verifyWithInlineConfigParserTwice(String filePath, String... expected)
333             throws Exception {
334         final TestInputConfiguration testInputConfiguration =
335                 InlineConfigParser.parse(filePath);
336         final DefaultConfiguration parsedConfig =
337                 testInputConfiguration.createConfiguration();
338         verifyViolations(parsedConfig, filePath, testInputConfiguration.getViolations());
339         verify(parsedConfig, filePath, expected);
340     }
341 
342     /**
343      * Performs verification of the file with the given file name. Uses specified configuration.
344      * Expected messages are represented by the array of strings.
345      * This implementation uses overloaded
346      * {@link AbstractModuleTestSupport#verify(Checker, File[], String, String...)} method inside.
347      *
348      * @param config configuration.
349      * @param fileName file name to verify.
350      * @param expected an array of expected messages.
351      * @throws Exception if exception occurs during verification process.
352      */
353     protected final void verify(Configuration config, String fileName, String... expected)
354             throws Exception {
355         verify(createChecker(config), fileName, fileName, expected);
356     }
357 
358     /**
359      * Performs verification of the file with the given file name.
360      * Uses provided {@link Checker} instance.
361      * Expected messages are represented by the array of strings.
362      * This implementation uses overloaded
363      * {@link AbstractModuleTestSupport#verify(Checker, String, String, String...)} method inside.
364      *
365      * @param checker {@link Checker} instance.
366      * @param fileName file name to verify.
367      * @param expected an array of expected messages.
368      * @throws Exception if exception occurs during verification process.
369      */
370     protected void verify(Checker checker, String fileName, String... expected)
371             throws Exception {
372         verify(checker, fileName, fileName, expected);
373     }
374 
375     /**
376      * Performs verification of the file with the given file name.
377      * Uses provided {@link Checker} instance.
378      * Expected messages are represented by the array of strings.
379      * This implementation uses overloaded
380      * {@link AbstractModuleTestSupport#verify(Checker, File[], String, String...)} method inside.
381      *
382      * @param checker {@link Checker} instance.
383      * @param processedFilename file name to verify.
384      * @param messageFileName message file name.
385      * @param expected an array of expected messages.
386      * @throws Exception if exception occurs during verification process.
387      */
388     protected final void verify(Checker checker,
389                           String processedFilename,
390                           String messageFileName,
391                           String... expected)
392             throws Exception {
393         verify(checker,
394                 new File[] {new File(processedFilename)},
395                 messageFileName, expected);
396     }
397 
398     /**
399      *  Performs verification of the given files against the array of
400      *  expected messages using the provided {@link Checker} instance.
401      *
402      *  @param checker {@link Checker} instance.
403      *  @param processedFiles list of files to verify.
404      *  @param messageFileName message file name.
405      *  @param expected an array of expected messages.
406      *  @throws Exception if exception occurs during verification process.
407      */
408     protected void verify(Checker checker,
409                           File[] processedFiles,
410                           String messageFileName,
411                           String... expected)
412             throws Exception {
413         final Map<String, List<String>> expectedViolations = new HashMap<>();
414         expectedViolations.put(messageFileName, Arrays.asList(expected));
415         verify(checker, processedFiles, expectedViolations);
416     }
417 
418     /**
419      * Performs verification of the given files.
420      *
421      * @param checker {@link Checker} instance
422      * @param processedFiles files to process.
423      * @param expectedViolations a map of expected violations per files.
424      * @throws Exception if exception occurs during verification process.
425      */
426     protected final void verify(Checker checker,
427                           File[] processedFiles,
428                           Map<String, List<String>> expectedViolations)
429             throws Exception {
430         stream.flush();
431         stream.reset();
432         final List<File> theFiles = new ArrayList<>();
433         Collections.addAll(theFiles, processedFiles);
434         final int errs = checker.process(theFiles);
435 
436         // process each of the lines
437         final Map<String, List<String>> actualViolations = getActualViolations(errs);
438         final Map<String, List<String>> realExpectedViolations =
439                 Maps.filterValues(expectedViolations, input -> !input.isEmpty());
440 
441         assertWithMessage("Files with expected violations and actual violations differ.")
442             .that(actualViolations.keySet())
443             .isEqualTo(realExpectedViolations.keySet());
444 
445         realExpectedViolations.forEach((fileName, violationList) -> {
446             assertWithMessage("Violations for %s differ.", fileName)
447                 .that(actualViolations.get(fileName))
448                 .containsExactlyElementsIn(violationList);
449         });
450 
451         checker.destroy();
452     }
453 
454     /**
455      * Runs 'verifyWithInlineConfigParser' with limited stack size and time duration.
456      *
457      * @param fileName file name to verify.
458      * @param expected an array of expected messages.
459      * @throws Exception if exception occurs during verification process.
460      */
461     protected final void verifyWithLimitedResources(String fileName, String... expected)
462             throws Exception {
463         // We return null here, which gives us a result to make an assertion about
464         final Void result = TestUtil.getResultWithLimitedResources(() -> {
465             verifyWithInlineConfigParser(fileName, expected);
466             return null;
467         });
468         assertWithMessage("Verify should complete successfully.")
469                 .that(result)
470                 .isNull();
471     }
472 
473     /**
474      * Executes given config on a list of files only. Does not verify violations.
475      *
476      * @param config check configuration
477      * @param filenames names of files to process
478      * @throws Exception if there is a problem during checker configuration
479      */
480     protected final void execute(Configuration config, String... filenames) throws Exception {
481         final Checker checker = createChecker(config);
482         final List<File> files = Arrays.stream(filenames)
483                 .map(File::new)
484                 .collect(Collectors.toUnmodifiableList());
485         checker.process(files);
486         checker.destroy();
487     }
488 
489     /**
490      * Executes given config on a list of files only. Does not verify violations.
491      *
492      * @param checker check configuration
493      * @param filenames names of files to process
494      * @throws Exception if there is a problem during checker configuration
495      */
496     protected static void execute(Checker checker, String... filenames) throws Exception {
497         final List<File> files = Arrays.stream(filenames)
498                 .map(File::new)
499                 .collect(Collectors.toUnmodifiableList());
500         checker.process(files);
501         checker.destroy();
502     }
503 
504     /**
505      * Performs verification of violation lines.
506      *
507      * @param config parsed config.
508      * @param file file path.
509      * @param testInputViolations List of TestInputViolation objects.
510      * @throws Exception if exception occurs during verification process.
511      */
512     private void verifyViolations(Configuration config,
513                                   String file,
514                                   List<TestInputViolation> testInputViolations)
515             throws Exception {
516         final List<String> actualViolations = getActualViolationsForFile(config, file);
517         final List<Integer> actualViolationLines = actualViolations.stream()
518                 .map(violation -> violation.substring(0, violation.indexOf(':')))
519                 .map(Integer::valueOf)
520                 .collect(Collectors.toUnmodifiableList());
521         final List<Integer> expectedViolationLines = testInputViolations.stream()
522                 .map(TestInputViolation::getLineNo)
523                 .collect(Collectors.toUnmodifiableList());
524         assertWithMessage("Violation lines for %s differ.", file)
525                 .that(actualViolationLines)
526                 .isEqualTo(expectedViolationLines);
527         for (int index = 0; index < actualViolations.size(); index++) {
528             assertWithMessage("Actual and expected violations differ.")
529                     .that(actualViolations.get(index))
530                     .matches(testInputViolations.get(index).toRegex());
531         }
532     }
533 
534     /**
535      * Performs verification of violation lines.
536      *
537      * @param file file path.
538      * @param testInputViolations List of TestInputViolation objects.
539      * @param actualViolations for a file
540      */
541     private static void verifyViolations(String file,
542                                   List<TestInputViolation> testInputViolations,
543                                   List<String> actualViolations) {
544         final List<Integer> actualViolationLines = actualViolations.stream()
545                 .map(violation -> violation.substring(0, violation.indexOf(':')))
546                 .map(Integer::valueOf)
547                 .collect(Collectors.toUnmodifiableList());
548         final List<Integer> expectedViolationLines = testInputViolations.stream()
549                 .map(TestInputViolation::getLineNo)
550                 .collect(Collectors.toUnmodifiableList());
551         assertWithMessage("Violation lines for %s differ.", file)
552                 .that(actualViolationLines)
553                 .isEqualTo(expectedViolationLines);
554         for (int index = 0; index < actualViolations.size(); index++) {
555             assertWithMessage("Actual and expected violations differ.")
556                     .that(actualViolations.get(index))
557                     .matches(testInputViolations.get(index).toRegex());
558         }
559     }
560 
561     /**
562      * Tests the file with the check config.
563      *
564      * @param config check configuration.
565      * @param file input file path.
566      * @return list of actual violations.
567      * @throws Exception if exception occurs during verification process.
568      */
569     private List<String> getActualViolationsForFile(Configuration config,
570                                                     String file) throws Exception {
571         stream.flush();
572         stream.reset();
573         final List<File> files = Collections.singletonList(new File(file));
574         final Checker checker = createChecker(config);
575         final Map<String, List<String>> actualViolations =
576                 getActualViolations(checker.process(files));
577         checker.destroy();
578         return actualViolations.getOrDefault(file, new ArrayList<>());
579     }
580 
581     /**
582      * Returns the actual violations for each file that has been checked against {@link Checker}.
583      * Each file is mapped to their corresponding violation messages. Reads input stream for these
584      * messages using instance of {@link InputStreamReader}.
585      *
586      * @param errorCount count of errors after checking set of files against {@link Checker}.
587      * @return a {@link Map} object containing file names and the corresponding violation messages.
588      * @throws IOException exception can occur when reading input stream.
589      */
590     private Map<String, List<String>> getActualViolations(int errorCount) throws IOException {
591         // process each of the lines
592         try (ByteArrayInputStream inputStream =
593                 new ByteArrayInputStream(stream.toByteArray());
594             LineNumberReader lnr = new LineNumberReader(
595                 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
596             final Map<String, List<String>> actualViolations = new HashMap<>();
597             for (String line = lnr.readLine(); line != null && lnr.getLineNumber() <= errorCount;
598                  line = lnr.readLine()) {
599                 // have at least 2 characters before the splitting colon,
600                 // to not split after the drive letter on Windows
601                 final String[] actualViolation = line.split("(?<=.{2}):", 2);
602                 final String actualViolationFileName = actualViolation[0];
603                 final String actualViolationMessage = actualViolation[1];
604 
605                 actualViolations
606                         .computeIfAbsent(actualViolationFileName, key -> new ArrayList<>())
607                         .add(actualViolationMessage);
608             }
609 
610             return actualViolations;
611         }
612     }
613 
614     /**
615      * Gets the check message 'as is' from appropriate 'messages.properties'
616      * file.
617      *
618      * @param messageKey the key of message in 'messages.properties' file.
619      * @param arguments  the arguments of message in 'messages.properties' file.
620      * @return The message of the check with the arguments applied.
621      */
622     protected final String getCheckMessage(String messageKey, Object... arguments) {
623         return internalGetCheckMessage(getMessageBundle(), messageKey, arguments);
624     }
625 
626     /**
627      * Gets the check message 'as is' from appropriate 'messages.properties'
628      * file.
629      *
630      * @param clazz the related check class.
631      * @param messageKey the key of message in 'messages.properties' file.
632      * @param arguments the arguments of message in 'messages.properties' file.
633      * @return The message of the check with the arguments applied.
634      */
635     protected static String getCheckMessage(
636             Class<?> clazz, String messageKey, Object... arguments) {
637         return internalGetCheckMessage(getMessageBundle(clazz.getName()), messageKey, arguments);
638     }
639 
640     /**
641      * Gets the check message 'as is' from appropriate 'messages.properties'
642      * file.
643      *
644      * @param messageBundle the bundle name.
645      * @param messageKey the key of message in 'messages.properties' file.
646      * @param arguments the arguments of message in 'messages.properties' file.
647      * @return The message of the check with the arguments applied.
648      */
649     private static String internalGetCheckMessage(
650             String messageBundle, String messageKey, Object... arguments) {
651         final ResourceBundle resourceBundle = ResourceBundle.getBundle(
652                 messageBundle,
653                 Locale.ROOT,
654                 Thread.currentThread().getContextClassLoader(),
655                 new Utf8Control());
656         final String pattern = resourceBundle.getString(messageKey);
657         final MessageFormat formatter = new MessageFormat(pattern, Locale.ROOT);
658         return formatter.format(arguments);
659     }
660 
661     /**
662      * Returns message bundle for a class specified by its class name.
663      *
664      * @return a string of message bundles for the class using class name.
665      */
666     private String getMessageBundle() {
667         final String className = getClass().getName();
668         return getMessageBundle(className);
669     }
670 
671     /**
672      * Returns message bundles for a class by providing class name.
673      *
674      * @param className name of the class.
675      * @return message bundles containing package name.
676      */
677     private static String getMessageBundle(String className) {
678         final String messageBundle;
679         final String messages = "messages";
680         final int endIndex = className.lastIndexOf('.');
681         final Map<String, String> messageBundleMappings = new HashMap<>();
682         messageBundleMappings.put("SeverityMatchFilterExamplesTest",
683                 "com.puppycrawl.tools.checkstyle.checks.naming.messages");
684 
685         if (endIndex < 0) {
686             messageBundle = messages;
687         }
688         else {
689             final String packageName = className.substring(0, endIndex);
690             if ("com.puppycrawl.tools.checkstyle.filters".equals(packageName)) {
691                 messageBundle = messageBundleMappings.get(className.substring(endIndex + 1));
692             }
693             else {
694                 messageBundle = packageName + "." + messages;
695             }
696         }
697         return messageBundle;
698     }
699 
700     /**
701      * Remove suppressed violation messages from actual violation messages.
702      *
703      * @param actualViolations actual violation messages
704      * @param suppressedViolations suppressed violation messages
705      * @return an array of actual violation messages minus suppressed violation messages
706      */
707     protected static String[] removeSuppressed(String[] actualViolations,
708                                                String... suppressedViolations) {
709         final List<String> actualViolationsList =
710             Arrays.stream(actualViolations).collect(Collectors.toCollection(ArrayList::new));
711         actualViolationsList.removeAll(Arrays.asList(suppressedViolations));
712         return actualViolationsList.toArray(CommonUtil.EMPTY_STRING_ARRAY);
713     }
714 
715 }