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.bdd;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.StringReader;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.Paths;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  import java.util.Properties;
35  import java.util.Set;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  import java.util.stream.Collectors;
39  
40  import org.xml.sax.InputSource;
41  
42  import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
43  import com.puppycrawl.tools.checkstyle.PropertiesExpander;
44  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
45  import com.puppycrawl.tools.checkstyle.api.Configuration;
46  
47  public final class InlineConfigParser {
48  
49      /** A pattern matching the symbol: "\" or "/". */
50      private static final Pattern SLASH_PATTERN = Pattern.compile("[\\\\/]");
51  
52      /**
53       * Pattern for lines under
54       * {@link InlineConfigParser#VIOLATIONS_SOME_LINES_ABOVE_PATTERN}.
55       */
56      private static final Pattern VIOLATION_MESSAGE_PATTERN = Pattern
57              .compile(".*//\\s*(?:['\"](.*)['\"])?$");
58      /**
59       * A pattern that matches the following comments formats.
60       * <ol>
61       *     <li> // violation </li>
62       *     <li> // violation, 'violation message' </li>
63       *     <li> // violation 'violation messages' </li>
64       *     <li> // violation, "violation messages" </li>
65       * </ol>
66       *
67       * <p>
68       * This pattern will not match the following formats.
69       * <ol>
70       *     <li> // violation, explanation </li>
71       *     <li> // violation, explanation, 'violation message' </li>
72       * </ol>
73       *
74       * These are matched by
75       * {@link InlineConfigParser#VIOLATION_WITH_EXPLANATION_PATTERN}.
76       * </p>
77       */
78      private static final Pattern VIOLATION_PATTERN = Pattern
79              .compile(".*//\\s*violation,?\\s*(?:['\"](.*)['\"])?$");
80  
81      /** A pattern to find the string: "// violation above". */
82      private static final Pattern VIOLATION_ABOVE_PATTERN = Pattern
83              .compile(".*//\\s*violation above,?\\s*(?:['\"](.*)['\"])?$");
84  
85      /** A pattern to find the string: "// violation below". */
86      private static final Pattern VIOLATION_BELOW_PATTERN = Pattern
87              .compile(".*//\\s*violation below,?\\s*(?:['\"](.*)['\"])?$");
88  
89      /** A pattern to find the string: "// violation above, explanation". */
90      private static final Pattern VIOLATION_ABOVE_WITH_EXPLANATION_PATTERN = Pattern
91              .compile(".*//\\s*violation above,\\s.+\\s(?:['\"](.*)['\"])?$");
92  
93      /** A pattern to find the string: "// violation below, explanation". */
94      private static final Pattern VIOLATION_BELOW_WITH_EXPLANATION_PATTERN = Pattern
95              .compile(".*//\\s*violation below,\\s.+\\s(?:['\"](.*)['\"])?$");
96  
97      /** A pattern to find the string: "// violation, explanation". */
98      private static final Pattern VIOLATION_WITH_EXPLANATION_PATTERN = Pattern
99              .compile(".*//\\s*violation,\\s+(?:.*)?$");
100 
101     /** A pattern to find the string: "// X violations". */
102     private static final Pattern MULTIPLE_VIOLATIONS_PATTERN = Pattern
103             .compile(".*//\\s*(\\d+) violations$");
104 
105     /** A pattern to find the string: "// X violations above". */
106     private static final Pattern MULTIPLE_VIOLATIONS_ABOVE_PATTERN = Pattern
107             .compile(".*//\\s*(\\d+) violations above$");
108 
109     /** A pattern to find the string: "// X violations below". */
110     private static final Pattern MULTIPLE_VIOLATIONS_BELOW_PATTERN = Pattern
111             .compile(".*//\\s*(\\d+) violations below$");
112 
113     /** A pattern to find the string: "// filtered violation". */
114     private static final Pattern FILTERED_VIOLATION_PATTERN = Pattern
115             .compile(".*//\\s*filtered violation\\s*(?:['\"](.*)['\"])?$");
116 
117     /** A pattern to find the string: "// filtered violation above". */
118     private static final Pattern FILTERED_VIOLATION_ABOVE_PATTERN = Pattern
119             .compile(".*//\\s*filtered violation above\\s*(?:['\"](.*)['\"])?$");
120 
121     /** A pattern to find the string: "// filtered violation below". */
122     private static final Pattern FILTERED_VIOLATION_BELOW_PATTERN = Pattern
123             .compile(".*//\\s*filtered violation below\\s*(?:['\"](.*)['\"])?$");
124 
125     /** A pattern to find the string: "// violation X lines above". */
126     private static final Pattern VIOLATION_SOME_LINES_ABOVE_PATTERN = Pattern
127             .compile(".*//\\s*violation (\\d+) lines above\\s*(?:['\"](.*)['\"])?$");
128 
129     /** A pattern to find the string: "// violation X lines below". */
130     private static final Pattern VIOLATION_SOME_LINES_BELOW_PATTERN = Pattern
131             .compile(".*//\\s*violation (\\d+) lines below\\s*(?:['\"](.*)['\"])?$");
132 
133     /**
134      * <p>
135      * Multiple violations for above line. Messages are X lines below.
136      * {@code
137      *   // X violations above:
138      *   //                    'violation message1'
139      *   //                    'violation messageX'
140      * }
141      *
142      * Messages are matched by {@link InlineConfigParser#VIOLATION_MESSAGE_PATTERN}
143      * </p>
144      */
145     private static final Pattern VIOLATIONS_ABOVE_PATTERN_WITH_MESSAGES = Pattern
146             .compile(".*//\\s*(\\d+) violations above:$");
147 
148     /**
149      * <p>
150      * Multiple violations for line. Violations are Y lines above, messages are X lines below.
151      * {@code
152      *   // X violations Y lines above:
153      *   //                            'violation message1'
154      *   //                            'violation messageX'
155      * }
156      *
157      * Messages are matched by {@link InlineConfigParser#VIOLATION_MESSAGE_PATTERN}
158      * </p>
159      */
160     private static final Pattern VIOLATIONS_SOME_LINES_ABOVE_PATTERN = Pattern
161             .compile(".*//\\s*(\\d+) violations (\\d+) lines above:$");
162 
163     /**
164      * <p>
165      * Multiple violations for line. Violations are Y lines below, messages are X lines below.
166      * {@code
167      *   // X violations Y lines below:
168      *   //                            'violation message1'
169      *   //                            'violation messageX'
170      * }
171      *
172      * Messages are matched by {@link InlineConfigParser#VIOLATION_MESSAGE_PATTERN}
173      * </p>
174      */
175     private static final Pattern VIOLATIONS_SOME_LINES_BELOW_PATTERN = Pattern
176             .compile(".*//\\s*(\\d+) violations (\\d+) lines below:$");
177 
178     /** A pattern that matches any comment by default. */
179     private static final Pattern VIOLATION_DEFAULT = Pattern
180             .compile("//.*violation.*");
181 
182     /** The String "(null)". */
183     private static final String NULL_STRING = "(null)";
184 
185     private static final String LATEST_DTD = String.format(Locale.ROOT,
186             "<!DOCTYPE module PUBLIC \"%s\" \"%s\">%n",
187             ConfigurationLoader.DTD_PUBLIC_CS_ID_1_3,
188             ConfigurationLoader.DTD_PUBLIC_CS_ID_1_3);
189 
190     /**
191      *  Inlined configs can not be used in non-java checks, as Inlined config is java style
192      *  multiline comment.
193      *  Such check files needs to be permanently suppressed.
194      */
195     private static final Set<String> PERMANENT_SUPPRESSED_CHECKS = Set.of(
196             // Inlined config is not supported for non java files.
197             "com.puppycrawl.tools.checkstyle.checks.OrderedPropertiesCheck",
198             "com.puppycrawl.tools.checkstyle.checks.UniquePropertiesCheck",
199             "com.puppycrawl.tools.checkstyle.checks.TranslationCheck"
200     );
201 
202     /**
203      *  Checks in which violation message is not specified in input files.
204      *  Until <a href="https://github.com/checkstyle/checkstyle/issues/15456">#15456</a>.
205      */
206     private static final Set<String> SUPPRESSED_CHECKS = Set.of(
207             "com.puppycrawl.tools.checkstyle.checks.annotation.PackageAnnotationCheck",
208             "com.puppycrawl.tools.checkstyle.checks.annotation.SuppressWarningsCheck",
209             "com.puppycrawl.tools.checkstyle.checks.ArrayTypeStyleCheck",
210             "com.puppycrawl.tools.checkstyle.checks.AvoidEscapedUnicodeCharactersCheck",
211             "com.puppycrawl.tools.checkstyle.checks.blocks.EmptyCatchBlockCheck",
212             "com.puppycrawl.tools.checkstyle.checks.blocks.NeedBracesCheck",
213             "com.puppycrawl.tools.checkstyle.checks.coding.ArrayTrailingCommaCheck",
214             "com.puppycrawl.tools.checkstyle.checks.coding.AvoidInlineConditionalsCheck",
215             "com.puppycrawl.tools.checkstyle.checks.coding"
216                     + ".AvoidNoArgumentSuperConstructorCallCheck",
217             "com.puppycrawl.tools.checkstyle.checks.coding.CovariantEqualsCheck",
218             "com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck",
219             "com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck",
220             "com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck",
221             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalCatchCheck",
222             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalInstantiationCheck",
223             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalThrowsCheck",
224             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalTokenCheck",
225             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalTokenTextCheck",
226             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalTypeCheck",
227             "com.puppycrawl.tools.checkstyle.checks.coding.InnerAssignmentCheck",
228             "com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
229             "com.puppycrawl.tools.checkstyle.checks.coding.MatchXpathCheck",
230             "com.puppycrawl.tools.checkstyle.checks.coding.MissingCtorCheck",
231             "com.puppycrawl.tools.checkstyle.checks.coding.MissingSwitchDefaultCheck",
232             "com.puppycrawl.tools.checkstyle.checks.coding.ModifiedControlVariableCheck",
233             "com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheck",
234             "com.puppycrawl.tools.checkstyle.checks.coding.NestedForDepthCheck",
235             "com.puppycrawl.tools.checkstyle.checks.coding.NestedIfDepthCheck",
236             "com.puppycrawl.tools.checkstyle.checks.coding.NestedTryDepthCheck",
237             "com.puppycrawl.tools.checkstyle.checks.coding.NoArrayTrailingCommaCheck",
238             "com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheck",
239             "com.puppycrawl.tools.checkstyle.checks.coding.NoEnumTrailingCommaCheck",
240             "com.puppycrawl.tools.checkstyle.checks.coding.NoFinalizerCheck",
241             "com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheck",
242             "com.puppycrawl.tools.checkstyle.checks.coding.OverloadMethodsDeclarationOrderCheck",
243             "com.puppycrawl.tools.checkstyle.checks.coding.ParameterAssignmentCheck",
244             "com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanExpressionCheck",
245             "com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanReturnCheck",
246             "com.puppycrawl.tools.checkstyle.checks.coding.StringLiteralEqualityCheck",
247             "com.puppycrawl.tools.checkstyle.checks.coding.SuperCloneCheck",
248             "com.puppycrawl.tools.checkstyle.checks.coding.SuperFinalizeCheck",
249             "com.puppycrawl.tools.checkstyle.checks.coding"
250                     + ".UnnecessarySemicolonAfterOuterTypeDeclarationCheck",
251             "com.puppycrawl.tools.checkstyle.checks.coding"
252                     + ".UnnecessarySemicolonAfterTypeMemberDeclarationCheck",
253             "com.puppycrawl.tools.checkstyle.checks.coding"
254                     + ".UnnecessarySemicolonInEnumerationCheck",
255             "com.puppycrawl.tools.checkstyle.checks.coding"
256                     + ".UnnecessarySemicolonInTryWithResourcesCheck",
257             "com.puppycrawl.tools.checkstyle.checks.coding"
258                     + ".UnusedCatchParameterShouldBeUnnamedCheck",
259             "com.puppycrawl.tools.checkstyle.checks.design.DesignForExtensionCheck",
260             "com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck",
261             "com.puppycrawl.tools.checkstyle.checks.design.InnerTypeLastCheck",
262             "com.puppycrawl.tools.checkstyle.checks.design.InterfaceIsTypeCheck",
263             "com.puppycrawl.tools.checkstyle.checks.design.MutableExceptionCheck",
264             "com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck",
265             "com.puppycrawl.tools.checkstyle.checks.design.SealedShouldHavePermitsListCheck",
266             "com.puppycrawl.tools.checkstyle.checks.design.ThrowsCountCheck",
267             "com.puppycrawl.tools.checkstyle.checks.design.VisibilityModifierCheck",
268             "com.puppycrawl.tools.checkstyle.checks.FinalParametersCheck",
269             "com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck",
270             "com.puppycrawl.tools.checkstyle.checks.imports.AvoidStaticImportCheck",
271             "com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck",
272             "com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck",
273             "com.puppycrawl.tools.checkstyle.checks.javadoc."
274                     + "AbstractJavadocCheckTest$TokenIsNotInAcceptablesCheck",
275             "com.puppycrawl.tools.checkstyle.checks.javadoc.AtclauseOrderCheck",
276             "com.puppycrawl.tools.checkstyle.checks.javadoc.InvalidJavadocPositionCheck",
277             "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocBlockTagLocationCheck",
278             "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMissingLeadingAsteriskCheck",
279             "com.puppycrawl.tools.checkstyle.checks.javadoc"
280                     + ".JavadocMissingWhitespaceAfterAsteriskCheck",
281             "com.puppycrawl.tools.checkstyle.checks.javadoc"
282                     + ".JavadocTagContinuationIndentationCheck",
283             "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck",
284             "com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck",
285             "com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocPackageCheck",
286             "com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck",
287             "com.puppycrawl.tools.checkstyle.checks.javadoc.NonEmptyAtclauseDescriptionCheck",
288             "com.puppycrawl.tools.checkstyle.checks.javadoc"
289                     + ".RequireEmptyLineBeforeBlockTagGroupCheck",
290             "com.puppycrawl.tools.checkstyle.checks.javadoc.SingleLineJavadocCheck",
291             "com.puppycrawl.tools.checkstyle.checks.metrics.BooleanExpressionComplexityCheck",
292             "com.puppycrawl.tools.checkstyle.checks.metrics.ClassDataAbstractionCouplingCheck",
293             "com.puppycrawl.tools.checkstyle.checks.metrics.ClassFanOutComplexityCheck",
294             "com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheck",
295             "com.puppycrawl.tools.checkstyle.checks.metrics.NPathComplexityCheck",
296             "com.puppycrawl.tools.checkstyle.checks.modifier.ClassMemberImpliedModifierCheck",
297             "com.puppycrawl.tools.checkstyle.checks.modifier.InterfaceMemberImpliedModifierCheck",
298             "com.puppycrawl.tools.checkstyle.checks.modifier.RedundantModifierCheck",
299             "com.puppycrawl.tools.checkstyle.checks.naming.AbbreviationAsWordInNameCheck",
300             "com.puppycrawl.tools.checkstyle.checks.naming.CatchParameterNameCheck",
301             "com.puppycrawl.tools.checkstyle.checks.naming.ClassTypeParameterNameCheck",
302             "com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck",
303             "com.puppycrawl.tools.checkstyle.checks.naming.IllegalIdentifierNameCheck",
304             "com.puppycrawl.tools.checkstyle.checks.naming.InterfaceTypeParameterNameCheck",
305             "com.puppycrawl.tools.checkstyle.checks.naming.LambdaParameterNameCheck",
306             "com.puppycrawl.tools.checkstyle.checks.naming.LocalFinalVariableNameCheck",
307             "com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck",
308             "com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck",
309             "com.puppycrawl.tools.checkstyle.checks.naming.MethodNameCheck",
310             "com.puppycrawl.tools.checkstyle.checks.naming.MethodTypeParameterNameCheck",
311             "com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck",
312             "com.puppycrawl.tools.checkstyle.checks.naming.ParameterNameCheck",
313             "com.puppycrawl.tools.checkstyle.checks.naming.PatternVariableNameCheck",
314             "com.puppycrawl.tools.checkstyle.checks.naming.RecordComponentNameCheck",
315             "com.puppycrawl.tools.checkstyle.checks.naming.RecordTypeParameterNameCheck",
316             "com.puppycrawl.tools.checkstyle.checks.naming.StaticVariableNameCheck",
317             "com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck",
318             "com.puppycrawl.tools.checkstyle.checks.NoCodeInFileCheck",
319             "com.puppycrawl.tools.checkstyle.checks.OuterTypeFilenameCheck",
320             "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpMultilineCheck",
321             "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck",
322             "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck",
323             "com.puppycrawl.tools.checkstyle.checks.sizes.AnonInnerLengthCheck",
324             "com.puppycrawl.tools.checkstyle.checks.sizes.ExecutableStatementCountCheck",
325             "com.puppycrawl.tools.checkstyle.checks.sizes.FileLengthCheck",
326             "com.puppycrawl.tools.checkstyle.checks.sizes.LambdaBodyLengthCheck",
327             "com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck",
328             "com.puppycrawl.tools.checkstyle.checks.sizes.MethodLengthCheck",
329             "com.puppycrawl.tools.checkstyle.checks.sizes.OuterTypeNumberCheck",
330             "com.puppycrawl.tools.checkstyle.checks.sizes.ParameterNumberCheck",
331             "com.puppycrawl.tools.checkstyle.checks.sizes.RecordComponentNumberCheck",
332             "com.puppycrawl.tools.checkstyle.checks.TodoCommentCheck",
333             "com.puppycrawl.tools.checkstyle.checks.TrailingCommentCheck",
334             "com.puppycrawl.tools.checkstyle.checks.UncommentedMainCheck",
335             "com.puppycrawl.tools.checkstyle.checks.UpperEllCheck",
336             "com.puppycrawl.tools.checkstyle.checks.whitespace.NoLineWrapCheck",
337             "com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceAfterCheck",
338             "com.puppycrawl.tools.checkstyle.checks.whitespace."
339                     + "NoWhitespaceBeforeCaseDefaultColonCheck",
340             "com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceBeforeCheck",
341             "com.puppycrawl.tools.checkstyle.checks.whitespace.ParenPadCheck",
342             "com.puppycrawl.tools.checkstyle.checks.whitespace.SingleSpaceSeparatorCheck",
343             "com.puppycrawl.tools.checkstyle.meta.JavadocMetadataScraper",
344             "com.puppycrawl.tools.checkstyle.api.AbstractCheckTest$ViolationAstCheck",
345             "com.puppycrawl.tools.checkstyle.CheckerTest$VerifyPositionAfterTabFileSet"
346     );
347 
348     /** Stop instances being created. **/
349     private InlineConfigParser() {
350     }
351 
352     public static TestInputConfiguration parse(String inputFilePath) throws Exception {
353         return parse(inputFilePath, false);
354     }
355 
356     /**
357      * Parses the input file provided.
358      *
359      * @param inputFilePath the input file path.
360      * @param setFilteredViolations flag to set filtered violations.
361      * @throws Exception if unable to read file or file not formatted properly.
362      */
363     private static TestInputConfiguration parse(String inputFilePath,
364                                                 boolean setFilteredViolations) throws Exception {
365         final TestInputConfiguration.Builder testInputConfigBuilder =
366                 new TestInputConfiguration.Builder();
367         final Path filePath = Paths.get(inputFilePath);
368         final List<String> lines = readFile(filePath);
369         try {
370             setModules(testInputConfigBuilder, inputFilePath, lines);
371         }
372         catch (Exception ex) {
373             throw new CheckstyleException("Config comment not specified properly in "
374                     + inputFilePath, ex);
375         }
376         try {
377             setViolations(testInputConfigBuilder, lines, setFilteredViolations);
378         }
379         catch (CheckstyleException ex) {
380             throw new CheckstyleException(ex.getMessage() + " in " + inputFilePath, ex);
381         }
382         return testInputConfigBuilder.build();
383     }
384 
385     public static List<TestInputViolation> getViolationsFromInputFile(String inputFilePath)
386             throws Exception {
387         final TestInputConfiguration.Builder testInputConfigBuilder =
388                 new TestInputConfiguration.Builder();
389         final Path filePath = Paths.get(inputFilePath);
390         final List<String> lines = readFile(filePath);
391 
392         try {
393             for (int lineNo = 0; lineNo < lines.size(); lineNo++) {
394                 setViolations(testInputConfigBuilder, lines, false, lineNo, true);
395             }
396         }
397         catch (CheckstyleException ex) {
398             throw new CheckstyleException(ex.getMessage() + " in " + inputFilePath, ex);
399         }
400 
401         return testInputConfigBuilder.build().getViolations();
402     }
403 
404     public static TestInputConfiguration parseWithFilteredViolations(String inputFilePath)
405             throws Exception {
406         return parse(inputFilePath, true);
407     }
408 
409     /**
410      * Parse the input file with configuration in xml header.
411      *
412      * @param inputFilePath the input file path.
413      * @throws Exception if unable to parse the xml header
414      */
415     public static TestInputConfiguration parseWithXmlHeader(String inputFilePath)
416             throws Exception {
417 
418         final Path filePath = Paths.get(inputFilePath);
419         final List<String> lines = readFile(filePath);
420         if (!checkIsXmlConfig(lines)) {
421             throw new CheckstyleException("Config cannot be parsed as xml.");
422         }
423 
424         final List<String> inlineConfig = getInlineConfig(lines);
425         final String stringXmlConfig = LATEST_DTD + String.join("", inlineConfig);
426         final InputSource inputSource = new InputSource(new StringReader(stringXmlConfig));
427         final Configuration xmlConfig = ConfigurationLoader.loadConfiguration(
428                 inputSource, new PropertiesExpander(System.getProperties()),
429                 ConfigurationLoader.IgnoredModulesOptions.EXECUTE
430         );
431         final String configName = xmlConfig.getName();
432         if (!"Checker".equals(configName)) {
433             throw new CheckstyleException(
434                     "First module should be Checker, but was " + configName);
435         }
436 
437         final TestInputConfiguration.Builder testInputConfigBuilder =
438                 new TestInputConfiguration.Builder();
439         testInputConfigBuilder.setXmlConfiguration(xmlConfig);
440         try {
441             setViolations(testInputConfigBuilder, lines, false);
442         }
443         catch (CheckstyleException ex) {
444             throw new CheckstyleException(ex.getMessage() + " in " + inputFilePath, ex);
445         }
446         return testInputConfigBuilder.buildWithXmlConfiguration();
447     }
448 
449     /**
450      * Check whether a file provides xml configuration.
451      *
452      * @param lines lines of the file
453      * @return true if a file provides xml configuration, otherwise false.
454      */
455     private static boolean checkIsXmlConfig(List<String> lines) {
456         return "/*xml".equals(lines.get(0));
457     }
458 
459     private static void setModules(TestInputConfiguration.Builder testInputConfigBuilder,
460                                    String inputFilePath, List<String> lines)
461             throws Exception {
462         if (!lines.get(0).startsWith("/*")) {
463             throw new CheckstyleException("Config not specified on top."
464                 + "Please see other inputs for examples of what is required.");
465         }
466 
467         final List<String> inlineConfig = getInlineConfig(lines);
468 
469         if (checkIsXmlConfig(lines)) {
470             final String stringXmlConfig = LATEST_DTD + String.join("", inlineConfig);
471             final InputSource inputSource = new InputSource(new StringReader(stringXmlConfig));
472             final Configuration xmlConfig = ConfigurationLoader.loadConfiguration(
473                 inputSource, new PropertiesExpander(System.getProperties()),
474                     ConfigurationLoader.IgnoredModulesOptions.EXECUTE
475             );
476             final String configName = xmlConfig.getName();
477             if (!"Checker".equals(configName)) {
478                 throw new CheckstyleException(
479                         "First module should be Checker, but was " + configName);
480             }
481             handleXmlConfig(testInputConfigBuilder, inputFilePath, xmlConfig.getChildren());
482         }
483         else {
484             handleKeyValueConfig(testInputConfigBuilder, inputFilePath, inlineConfig);
485         }
486     }
487 
488     private static List<String> getInlineConfig(List<String> lines) {
489         return lines.stream()
490                 .skip(1)
491                 .takeWhile(line -> !line.startsWith("*/"))
492                 .collect(Collectors.toUnmodifiableList());
493     }
494 
495     private static void handleXmlConfig(TestInputConfiguration.Builder testInputConfigBuilder,
496                                         String inputFilePath,
497                                         Configuration... modules)
498             throws CheckstyleException {
499 
500         for (Configuration module: modules) {
501             final String moduleName = module.getName();
502             if ("TreeWalker".equals(moduleName)) {
503                 handleXmlConfig(testInputConfigBuilder, inputFilePath, module.getChildren());
504             }
505             else {
506                 final ModuleInputConfiguration.Builder moduleInputConfigBuilder =
507                         new ModuleInputConfiguration.Builder();
508                 setModuleName(moduleInputConfigBuilder, inputFilePath, moduleName);
509                 setProperties(inputFilePath, module, moduleInputConfigBuilder);
510                 testInputConfigBuilder.addChildModule(moduleInputConfigBuilder.build());
511             }
512         }
513     }
514 
515     private static void handleKeyValueConfig(TestInputConfiguration.Builder testInputConfigBuilder,
516                                              String inputFilePath, List<String> lines)
517             throws CheckstyleException, IOException {
518         int lineNo = 0;
519         while (lineNo < lines.size()) {
520             final ModuleInputConfiguration.Builder moduleInputConfigBuilder =
521                     new ModuleInputConfiguration.Builder();
522             setModuleName(moduleInputConfigBuilder, inputFilePath, lines.get(lineNo));
523             setProperties(moduleInputConfigBuilder, inputFilePath, lines, lineNo + 1);
524             testInputConfigBuilder.addChildModule(moduleInputConfigBuilder.build());
525             do {
526                 lineNo++;
527             } while (lineNo < lines.size()
528                     && lines.get(lineNo).isEmpty()
529                     || !lines.get(lineNo - 1).isEmpty());
530         }
531     }
532 
533     private static String getFullyQualifiedClassName(String filePath, String moduleName)
534             throws CheckstyleException {
535         // This is a hack until https://github.com/checkstyle/checkstyle/issues/13845
536         final Map<String, String> moduleMappings = new HashMap<>();
537         moduleMappings.put("ParameterNumber",
538                 "com.puppycrawl.tools.checkstyle.checks.sizes.ParameterNumberCheck");
539         moduleMappings.put("SuppressWarningsHolder",
540                 "com.puppycrawl.tools.checkstyle.checks.SuppressWarningsHolder");
541         moduleMappings.put("SuppressWarningsFilter",
542                 "com.puppycrawl.tools.checkstyle.filters.SuppressWarningsFilter");
543         moduleMappings.put("MemberName",
544                 "com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck");
545         moduleMappings.put("ConstantName",
546                 "com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck");
547         moduleMappings.put("NoWhitespaceAfter",
548                 "com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceAfterCheck");
549         moduleMappings.put("SummaryJavadoc",
550                 "com.puppycrawl.tools.checkstyle.checks.javadoc.SummaryJavadocCheck");
551         moduleMappings.put("LineLength",
552                 "com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck");
553         moduleMappings.put("ParameterName",
554                 "com.puppycrawl.tools.checkstyle.checks.naming.ParameterNameCheck");
555         moduleMappings.put("MethodName",
556                 "com.puppycrawl.tools.checkstyle.checks.naming.MethodNameCheck");
557         moduleMappings.put("SuppressionXpathSingleFilter",
558                 "com.puppycrawl.tools.checkstyle.filters.SuppressionXpathSingleFilter");
559 
560         String fullyQualifiedClassName;
561         if (moduleMappings.containsKey(moduleName)) {
562             fullyQualifiedClassName = moduleMappings.get(moduleName);
563         }
564         else if (moduleName.startsWith("com.")) {
565             fullyQualifiedClassName = moduleName;
566         }
567         else {
568             final String path = SLASH_PATTERN.matcher(filePath).replaceAll(".");
569             final int endIndex = path.lastIndexOf(moduleName.toLowerCase(Locale.ROOT));
570             if (endIndex == -1) {
571                 throw new CheckstyleException("Unable to resolve module name: " + moduleName
572                 + ". Please check for spelling errors or specify fully qualified class name.");
573             }
574             final int beginIndex = path.indexOf("com.puppycrawl");
575             fullyQualifiedClassName = path.substring(beginIndex, endIndex) + moduleName;
576             if (!fullyQualifiedClassName.endsWith("Filter")) {
577                 fullyQualifiedClassName += "Check";
578             }
579         }
580         return fullyQualifiedClassName;
581     }
582 
583     private static String getFilePath(String fileName, String inputFilePath) {
584         final int lastSlashIndex = Math.max(inputFilePath.lastIndexOf('\\'),
585                 inputFilePath.lastIndexOf('/'));
586         final String root = inputFilePath.substring(0, lastSlashIndex + 1);
587         return root + fileName;
588     }
589 
590     private static String getResourcePath(String fileName, String inputFilePath) {
591         final String filePath = getUriPath(fileName, inputFilePath);
592         final int lastSlashIndex = filePath.lastIndexOf('/');
593         final String root = filePath.substring(filePath.indexOf("puppycrawl") - 5,
594                 lastSlashIndex + 1);
595         return root + fileName;
596     }
597 
598     private static String getUriPath(String fileName, String inputFilePath) {
599         return new File(getFilePath(fileName, inputFilePath)).toURI().toString();
600     }
601 
602     private static String getResolvedPath(String fileValue, String inputFilePath) {
603         final String resolvedFilePath;
604         if (fileValue.startsWith("(resource)")) {
605             resolvedFilePath =
606                     getResourcePath(fileValue.substring(fileValue.indexOf(')') + 1),
607                             inputFilePath);
608         }
609         else if (fileValue.startsWith("(uri)")) {
610             resolvedFilePath =
611                     getUriPath(fileValue.substring(fileValue.indexOf(')') + 1), inputFilePath);
612         }
613         else {
614             resolvedFilePath = getFilePath(fileValue, inputFilePath);
615         }
616         return resolvedFilePath;
617     }
618 
619     private static List<String> readFile(Path filePath) throws CheckstyleException {
620         try {
621             return Files.readAllLines(filePath);
622         }
623         catch (IOException ex) {
624             throw new CheckstyleException("Failed to read " + filePath, ex);
625         }
626     }
627 
628     private static void setModuleName(ModuleInputConfiguration.Builder moduleInputConfigBuilder,
629                                       String filePath, String moduleName)
630             throws CheckstyleException {
631         final String fullyQualifiedClassName = getFullyQualifiedClassName(filePath, moduleName);
632         moduleInputConfigBuilder.setModuleName(fullyQualifiedClassName);
633     }
634 
635     private static void setProperties(ModuleInputConfiguration.Builder inputConfigBuilder,
636                                       String inputFilePath,
637                                       List<String> lines,
638                                       int beginLineNo)
639                     throws IOException {
640         final StringBuilder stringBuilder = new StringBuilder(128);
641         int lineNo = beginLineNo;
642         for (String line = lines.get(lineNo); !line.isEmpty() && !"*/".equals(line);
643                 ++lineNo, line = lines.get(lineNo)) {
644             stringBuilder.append(line).append('\n');
645         }
646         final Properties properties = new Properties();
647         properties.load(new StringReader(stringBuilder.toString()));
648         for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
649             final String key = entry.getKey().toString();
650             final String value = entry.getValue().toString();
651             if (key.startsWith("message.")) {
652                 inputConfigBuilder.addModuleMessage(key.substring(8), value);
653             }
654             else if (value.startsWith("(file)")) {
655                 final String fileName = value.substring(value.indexOf(')') + 1);
656                 final String filePath = getResolvedPath(fileName, inputFilePath);
657                 inputConfigBuilder.addNonDefaultProperty(key, filePath);
658             }
659             else if (value.startsWith("(default)")) {
660                 final String defaultValue = value.substring(value.indexOf(')') + 1);
661                 if (NULL_STRING.equals(defaultValue)) {
662                     inputConfigBuilder.addDefaultProperty(key, null);
663                 }
664                 else {
665                     inputConfigBuilder.addDefaultProperty(key, defaultValue);
666                 }
667             }
668             else {
669                 if (NULL_STRING.equals(value)) {
670                     inputConfigBuilder.addNonDefaultProperty(key, null);
671                 }
672                 else {
673                     inputConfigBuilder.addNonDefaultProperty(key, value);
674                 }
675             }
676         }
677     }
678 
679     private static void setProperties(String inputFilePath, Configuration module,
680                                       ModuleInputConfiguration.Builder moduleInputConfigBuilder)
681             throws CheckstyleException {
682         final String[] getPropertyNames = module.getPropertyNames();
683         for (final String propertyName : getPropertyNames) {
684             final String propertyValue = module.getProperty(propertyName);
685 
686             if ("file".equals(propertyName)) {
687                 final String filePath = getResolvedPath(propertyValue, inputFilePath);
688                 moduleInputConfigBuilder.addNonDefaultProperty(propertyName, filePath);
689             }
690             else {
691                 if (NULL_STRING.equals(propertyValue)) {
692                     moduleInputConfigBuilder.addNonDefaultProperty(propertyName, null);
693                 }
694                 else {
695                     moduleInputConfigBuilder.addNonDefaultProperty(propertyName, propertyValue);
696                 }
697             }
698         }
699 
700         final Map<String, String> messages = module.getMessages();
701         for (final Map.Entry<String, String> entry : messages.entrySet()) {
702             final String key = entry.getKey();
703             final String value = entry.getValue();
704             moduleInputConfigBuilder.addModuleMessage(key, value);
705         }
706     }
707 
708     private static void setViolations(TestInputConfiguration.Builder inputConfigBuilder,
709                                       List<String> lines, boolean useFilteredViolations)
710             throws CheckstyleException {
711         final List<ModuleInputConfiguration> moduleLists = inputConfigBuilder.getChildrenModules();
712         final boolean specifyViolationMessage = moduleLists.size() == 1
713                 && !PERMANENT_SUPPRESSED_CHECKS.contains(moduleLists.get(0).getModuleName())
714                 && !SUPPRESSED_CHECKS.contains(moduleLists.get(0).getModuleName());
715         for (int lineNo = 0; lineNo < lines.size(); lineNo++) {
716             setViolations(inputConfigBuilder, lines,
717                     useFilteredViolations, lineNo, specifyViolationMessage);
718         }
719     }
720 
721     /**
722      * Sets the violations.
723      *
724      * @param inputConfigBuilder the input file path.
725      * @param lines all the lines in the file.
726      * @param useFilteredViolations flag to set filtered violations.
727      * @param lineNo current line.
728      * @noinspection IfStatementWithTooManyBranches
729      * @noinspectionreason IfStatementWithTooManyBranches - complex logic of violation
730      *      parser requires giant if/else
731      * @throws CheckstyleException if violation message is not specified
732      */
733     // -@cs[ExecutableStatementCount] splitting this method is not reasonable.
734     // -@cs[JavaNCSS] splitting this method is not reasonable.
735     // -@cs[CyclomaticComplexity] splitting this method is not reasonable.
736     private static void setViolations(TestInputConfiguration.Builder inputConfigBuilder,
737                                       List<String> lines, boolean useFilteredViolations,
738                                       int lineNo, boolean specifyViolationMessage)
739             throws CheckstyleException {
740         final Matcher violationMatcher =
741                 VIOLATION_PATTERN.matcher(lines.get(lineNo));
742         final Matcher violationAboveMatcher =
743                 VIOLATION_ABOVE_PATTERN.matcher(lines.get(lineNo));
744         final Matcher violationBelowMatcher =
745                 VIOLATION_BELOW_PATTERN.matcher(lines.get(lineNo));
746         final Matcher violationAboveWithExplanationMatcher =
747                 VIOLATION_ABOVE_WITH_EXPLANATION_PATTERN.matcher(lines.get(lineNo));
748         final Matcher violationBelowWithExplanationMatcher =
749                 VIOLATION_BELOW_WITH_EXPLANATION_PATTERN.matcher(lines.get(lineNo));
750         final Matcher violationWithExplanationMatcher =
751                 VIOLATION_WITH_EXPLANATION_PATTERN.matcher(lines.get(lineNo));
752         final Matcher multipleViolationsMatcher =
753                 MULTIPLE_VIOLATIONS_PATTERN.matcher(lines.get(lineNo));
754         final Matcher multipleViolationsAboveMatcher =
755                 MULTIPLE_VIOLATIONS_ABOVE_PATTERN.matcher(lines.get(lineNo));
756         final Matcher multipleViolationsBelowMatcher =
757                 MULTIPLE_VIOLATIONS_BELOW_PATTERN.matcher(lines.get(lineNo));
758         final Matcher violationSomeLinesAboveMatcher =
759                 VIOLATION_SOME_LINES_ABOVE_PATTERN.matcher(lines.get(lineNo));
760         final Matcher violationSomeLinesBelowMatcher =
761                 VIOLATION_SOME_LINES_BELOW_PATTERN.matcher(lines.get(lineNo));
762         final Matcher violationsAboveMatcherWithMessages =
763                 VIOLATIONS_ABOVE_PATTERN_WITH_MESSAGES.matcher(lines.get(lineNo));
764         final Matcher violationsSomeLinesAboveMatcher =
765                 VIOLATIONS_SOME_LINES_ABOVE_PATTERN.matcher(lines.get(lineNo));
766         final Matcher violationsSomeLinesBelowMatcher =
767                 VIOLATIONS_SOME_LINES_BELOW_PATTERN.matcher(lines.get(lineNo));
768         final Matcher violationsDefault =
769                 VIOLATION_DEFAULT.matcher(lines.get(lineNo));
770         if (violationMatcher.matches()) {
771             final String violationMessage = violationMatcher.group(1);
772             final int violationLineNum = lineNo + 1;
773             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
774                     violationLineNum);
775             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
776         }
777         else if (violationAboveMatcher.matches()) {
778             final String violationMessage = violationAboveMatcher.group(1);
779             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage, lineNo);
780             inputConfigBuilder.addViolation(lineNo, violationMessage);
781         }
782         else if (violationBelowMatcher.matches()) {
783             final String violationMessage = violationBelowMatcher.group(1);
784             final int violationLineNum = lineNo + 2;
785             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
786                     violationLineNum);
787             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
788         }
789         else if (violationAboveWithExplanationMatcher.matches()) {
790             final String violationMessage = violationAboveWithExplanationMatcher.group(1);
791             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage, lineNo);
792             inputConfigBuilder.addViolation(lineNo, violationMessage);
793         }
794         else if (violationBelowWithExplanationMatcher.matches()) {
795             final String violationMessage = violationBelowWithExplanationMatcher.group(1);
796             final int violationLineNum = lineNo + 2;
797             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
798                     violationLineNum);
799             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
800         }
801         else if (violationWithExplanationMatcher.matches()) {
802             final int violationLineNum = lineNo + 1;
803             inputConfigBuilder.addViolation(violationLineNum, null);
804         }
805         else if (violationSomeLinesAboveMatcher.matches()) {
806             final String violationMessage = violationSomeLinesAboveMatcher.group(2);
807             final int linesAbove = Integer.parseInt(violationSomeLinesAboveMatcher.group(1)) - 1;
808             final int violationLineNum = lineNo - linesAbove;
809             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
810                     violationLineNum);
811             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
812         }
813         else if (violationSomeLinesBelowMatcher.matches()) {
814             final String violationMessage = violationSomeLinesBelowMatcher.group(2);
815             final int linesBelow = Integer.parseInt(violationSomeLinesBelowMatcher.group(1)) + 1;
816             final int violationLineNum = lineNo + linesBelow;
817             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
818                     violationLineNum);
819             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
820         }
821         else if (violationsAboveMatcherWithMessages.matches()) {
822             inputConfigBuilder.addViolations(
823                 getExpectedViolationsForSpecificLine(
824                     lines, lineNo, lineNo, violationsAboveMatcherWithMessages));
825         }
826         else if (violationsSomeLinesAboveMatcher.matches()) {
827             inputConfigBuilder.addViolations(
828                 getExpectedViolations(
829                     lines, lineNo, violationsSomeLinesAboveMatcher, true));
830         }
831         else if (violationsSomeLinesBelowMatcher.matches()) {
832             inputConfigBuilder.addViolations(
833                     getExpectedViolations(
834                             lines, lineNo, violationsSomeLinesBelowMatcher, false));
835         }
836         else if (multipleViolationsMatcher.matches()) {
837             Collections
838                     .nCopies(Integer.parseInt(multipleViolationsMatcher.group(1)), lineNo + 1)
839                     .forEach(actualLineNumber -> {
840                         inputConfigBuilder.addViolation(actualLineNumber, null);
841                     });
842         }
843         else if (multipleViolationsAboveMatcher.matches()) {
844             Collections
845                     .nCopies(Integer.parseInt(multipleViolationsAboveMatcher.group(1)), lineNo)
846                     .forEach(actualLineNumber -> {
847                         inputConfigBuilder.addViolation(actualLineNumber, null);
848                     });
849         }
850         else if (multipleViolationsBelowMatcher.matches()) {
851             Collections
852                     .nCopies(Integer.parseInt(multipleViolationsBelowMatcher.group(1)),
853                             lineNo + 2)
854                     .forEach(actualLineNumber -> {
855                         inputConfigBuilder.addViolation(actualLineNumber, null);
856                     });
857         }
858         else if (useFilteredViolations) {
859             setFilteredViolation(inputConfigBuilder, lineNo + 1,
860                     lines.get(lineNo), specifyViolationMessage);
861         }
862         else if (violationsDefault.matches()) {
863             final int violationLineNum = lineNo + 1;
864             inputConfigBuilder.addViolation(violationLineNum, null);
865         }
866     }
867 
868     private static List<TestInputViolation> getExpectedViolationsForSpecificLine(
869                                               List<String> lines, int lineNo, int violationLineNum,
870                                               Matcher matcher) {
871         final List<TestInputViolation> results = new ArrayList<>();
872 
873         final int expectedMessageCount =
874             Integer.parseInt(matcher.group(1));
875         for (int index = 1; index <= expectedMessageCount; index++) {
876             final String lineWithMessage = lines.get(lineNo + index);
877             final Matcher messageMatcher = VIOLATION_MESSAGE_PATTERN.matcher(lineWithMessage);
878             if (messageMatcher.find()) {
879                 final String violationMessage = messageMatcher.group(1);
880                 results.add(new TestInputViolation(violationLineNum, violationMessage));
881             }
882         }
883         if (results.size() != expectedMessageCount) {
884             final String message = String.format(Locale.ROOT,
885                 "Declared amount of violation messages at line %s is %s but found %s",
886                 lineNo + 1, expectedMessageCount, results.size());
887             throw new IllegalStateException(message);
888         }
889         return results;
890     }
891 
892     private static List<TestInputViolation> getExpectedViolations(
893                                               List<String> lines, int lineNo,
894                                               Matcher matcher, boolean isAbove) {
895         final int violationLine =
896             Integer.parseInt(matcher.group(2));
897         final int violationLineNum;
898         if (isAbove) {
899             violationLineNum = lineNo - violationLine + 1;
900         }
901         else {
902             violationLineNum = lineNo + violationLine + 1;
903         }
904         return getExpectedViolationsForSpecificLine(lines,
905             lineNo, violationLineNum, matcher);
906     }
907 
908     private static void setFilteredViolation(TestInputConfiguration.Builder inputConfigBuilder,
909                                              int lineNo, String line,
910                                              boolean specifyViolationMessage)
911             throws CheckstyleException {
912         final Matcher violationMatcher =
913                 FILTERED_VIOLATION_PATTERN.matcher(line);
914         final Matcher violationAboveMatcher =
915                 FILTERED_VIOLATION_ABOVE_PATTERN.matcher(line);
916         final Matcher violationBelowMatcher =
917                 FILTERED_VIOLATION_BELOW_PATTERN.matcher(line);
918         if (violationMatcher.matches()) {
919             final String violationMessage = violationMatcher.group(1);
920             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage, lineNo);
921             inputConfigBuilder.addFilteredViolation(lineNo, violationMessage);
922         }
923         else if (violationAboveMatcher.matches()) {
924             final String violationMessage = violationAboveMatcher.group(1);
925             final int violationLineNum = lineNo - 1;
926             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
927                     violationLineNum);
928             inputConfigBuilder.addFilteredViolation(violationLineNum, violationMessage);
929         }
930         else if (violationBelowMatcher.matches()) {
931             final String violationMessage = violationBelowMatcher.group(1);
932             final int violationLineNum = lineNo + 1;
933             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
934                     violationLineNum);
935             inputConfigBuilder.addFilteredViolation(violationLineNum, violationMessage);
936         }
937     }
938 
939     /**
940      * Check whether violation is specified along with {@code // violation} comment.
941      *
942      * @param shouldViolationMsgBeSpecified should violation messages be specified.
943      * @param violationMessage violation message
944      * @param lineNum line number
945      * @throws CheckstyleException if violation message is not specified
946      */
947     private static void checkWhetherViolationSpecified(boolean shouldViolationMsgBeSpecified,
948             String violationMessage, int lineNum) throws CheckstyleException {
949         if (shouldViolationMsgBeSpecified && violationMessage == null) {
950             throw new CheckstyleException(
951                     "Violation message should be specified on line " + lineNum);
952         }
953     }
954 }