View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.lang.reflect.Field;
26  import java.lang.reflect.Method;
27  import java.math.BigDecimal;
28  import java.nio.file.Files;
29  import java.nio.file.Path;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.BitSet;
33  import java.util.Collection;
34  import java.util.Collections;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Locale;
39  import java.util.Map;
40  import java.util.Properties;
41  import java.util.Set;
42  import java.util.regex.Matcher;
43  import java.util.regex.Pattern;
44  import java.util.stream.Collectors;
45  
46  import org.xml.sax.InputSource;
47  
48  import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
49  import com.puppycrawl.tools.checkstyle.PropertiesExpander;
50  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
51  import com.puppycrawl.tools.checkstyle.api.Configuration;
52  import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
53  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
54  
55  public final class InlineConfigParser {
56  
57      /** A pattern matching the symbol: "\" or "/". */
58      private static final Pattern SLASH_PATTERN = Pattern.compile("[\\\\/]");
59  
60      /**
61       * Pattern for lines under
62       * {@link InlineConfigParser#VIOLATIONS_SOME_LINES_ABOVE_PATTERN}.
63       */
64      private static final Pattern VIOLATION_MESSAGE_PATTERN = Pattern
65              .compile(".*//\\s*(?:['\"](.*)['\"])?$");
66      /**
67       * A pattern that matches the following comments formats.
68       * <ol>
69       *     <li> // violation </li>
70       *     <li> // violation, 'violation message' </li>
71       *     <li> // violation 'violation messages' </li>
72       *     <li> // violation, "violation messages" </li>
73       * </ol>
74       *
75       * <p>
76       * This pattern will not match the following formats.
77       * <ol>
78       *     <li> // violation, explanation </li>
79       *     <li> // violation, explanation, 'violation message' </li>
80       * </ol>
81       *
82       * These are matched by
83       * {@link InlineConfigParser#VIOLATION_WITH_EXPLANATION_PATTERN}.
84       * </p>
85       */
86      private static final Pattern VIOLATION_PATTERN = Pattern
87              .compile(".*//\\s*violation,?\\s*(?:['\"](.*)['\"])?$");
88  
89      /** A pattern to find the string: "// violation above". */
90      private static final Pattern VIOLATION_ABOVE_PATTERN = Pattern
91              .compile(".*//\\s*violation above,?\\s*(?:['\"](.*)['\"])?$");
92  
93      /** A pattern to find the string: "// violation below". */
94      private static final Pattern VIOLATION_BELOW_PATTERN = Pattern
95              .compile(".*//\\s*violation below,?\\s*(?:['\"](.*)['\"])?$");
96  
97      /** A pattern to find the string: "// violation above, explanation". */
98      private static final Pattern VIOLATION_ABOVE_WITH_EXPLANATION_PATTERN = Pattern
99              .compile(".*//\\s*violation above,\\s.+\\s(?:['\"](.*)['\"])?$");
100 
101     /** A pattern to find the string: "// violation below, explanation". */
102     private static final Pattern VIOLATION_BELOW_WITH_EXPLANATION_PATTERN = Pattern
103             .compile(".*//\\s*violation below,\\s.+\\s(?:['\"](.*)['\"])?$");
104 
105     /** A pattern to find the string: "// violation, explanation". */
106     private static final Pattern VIOLATION_WITH_EXPLANATION_PATTERN = Pattern
107             .compile(".*//\\s*violation,\\s+(?:.*)?$");
108 
109     /** A pattern to find the string: "// X violations". */
110     private static final Pattern MULTIPLE_VIOLATIONS_PATTERN = Pattern
111             .compile(".*//\\s*(\\d+) violations$");
112 
113     /** A pattern to find the string: "// X violations above". */
114     private static final Pattern MULTIPLE_VIOLATIONS_ABOVE_PATTERN = Pattern
115             .compile(".*//\\s*(\\d+) violations above$");
116 
117     /** A pattern to find the string: "// X violations below". */
118     private static final Pattern MULTIPLE_VIOLATIONS_BELOW_PATTERN = Pattern
119             .compile(".*//\\s*(\\d+) violations below$");
120 
121     /** A pattern to find the string: "// filtered violation". */
122     private static final Pattern FILTERED_VIOLATION_PATTERN = Pattern
123             .compile(".*//\\s*filtered violation\\s*(?:['\"](.*)['\"])?$");
124 
125     /** A pattern to find the string: "// filtered violation above". */
126     private static final Pattern FILTERED_VIOLATION_ABOVE_PATTERN = Pattern
127             .compile(".*//\\s*filtered violation above\\s*(?:['\"](.*)['\"])?$");
128 
129     /** A pattern to find the string: "// filtered violation below". */
130     private static final Pattern FILTERED_VIOLATION_BELOW_PATTERN = Pattern
131             .compile(".*//\\s*filtered violation below\\s*(?:['\"](.*)['\"])?$");
132 
133     /** A pattern to find the string: "// filtered violation X lines above". */
134     private static final Pattern FILTERED_VIOLATION_SOME_LINES_ABOVE_PATTERN = Pattern
135             .compile(".*//\\s*filtered violation (\\d+) lines above\\s*(?:['\"](.*)['\"])?$");
136 
137     /** A pattern to find the string: "// violation X lines above". */
138     private static final Pattern VIOLATION_SOME_LINES_ABOVE_PATTERN = Pattern
139             .compile(".*//\\s*violation (\\d+) lines above\\s*(?:['\"](.*)['\"])?$");
140 
141     /** A pattern to find the string: "// violation X lines below". */
142     private static final Pattern VIOLATION_SOME_LINES_BELOW_PATTERN = Pattern
143             .compile(".*//\\s*violation (\\d+) lines below\\s*(?:['\"](.*)['\"])?$");
144 
145     /**
146      * <div>
147      * Multiple violations for above line. Messages are X lines below.
148      * {@code
149      *   // X violations above:
150      *   //                    'violation message1'
151      *   //                    'violation messageX'
152      * }
153      *
154      * Messages are matched by {@link InlineConfigParser#VIOLATION_MESSAGE_PATTERN}
155      * </div>
156      */
157     private static final Pattern VIOLATIONS_ABOVE_PATTERN_WITH_MESSAGES = Pattern
158             .compile(".*//\\s*(\\d+) violations above:$");
159 
160     /**
161      * <div>
162      * Multiple violations for line. Violations are Y lines above, messages are X lines below.
163      * {@code
164      *   // X violations Y lines above:
165      *   //                            'violation message1'
166      *   //                            'violation messageX'
167      * }
168      *
169      * Messages are matched by {@link InlineConfigParser#VIOLATION_MESSAGE_PATTERN}
170      * </div>
171      */
172     private static final Pattern VIOLATIONS_SOME_LINES_ABOVE_PATTERN = Pattern
173             .compile(".*//\\s*(\\d+) violations (\\d+) lines above:$");
174 
175     /**
176      * <div>
177      * Multiple violations for line. Violations are Y lines below, messages are X lines below.
178      * {@code
179      *   // X violations Y lines below:
180      *   //                            'violation message1'
181      *   //                            'violation messageX'
182      * }
183      *
184      * Messages are matched by {@link InlineConfigParser#VIOLATION_MESSAGE_PATTERN}
185      * </div>
186      */
187     private static final Pattern VIOLATIONS_SOME_LINES_BELOW_PATTERN = Pattern
188             .compile(".*//\\s*(\\d+) violations (\\d+) lines below:$");
189 
190     /** A pattern that matches any comment by default. */
191     private static final Pattern VIOLATION_DEFAULT = Pattern
192             .compile("//.*violation.*");
193 
194     /** The String "(null)". */
195     private static final String NULL_STRING = "(null)";
196 
197     private static final String LATEST_DTD = String.format(Locale.ROOT,
198             "<!DOCTYPE module PUBLIC \"%s\" \"%s\">%n",
199             ConfigurationLoader.DTD_PUBLIC_CS_ID_1_3,
200             ConfigurationLoader.DTD_PUBLIC_CS_ID_1_3);
201 
202     /**
203      *  Inlined configs can not be used in non-java checks, as Inlined config is java style
204      *  multiline comment.
205      *  Such check files needs to be permanently suppressed.
206      */
207     private static final Set<String> PERMANENT_SUPPRESSED_CHECKS = Set.of(
208             // Inlined config is not supported for non java files.
209             "com.puppycrawl.tools.checkstyle.checks.OrderedPropertiesCheck",
210             "com.puppycrawl.tools.checkstyle.checks.UniquePropertiesCheck",
211             "com.puppycrawl.tools.checkstyle.checks.TranslationCheck"
212     );
213 
214     /**
215      *  Checks in which violation message is not specified in input files.
216      *  Until <a href="https://github.com/checkstyle/checkstyle/issues/15456">#15456</a>.
217      */
218     private static final Set<String> SUPPRESSED_CHECKS = Set.of(
219             "com.puppycrawl.tools.checkstyle.checks.AvoidEscapedUnicodeCharactersCheck",
220             "com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck",
221             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalInstantiationCheck",
222             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalTokenTextCheck",
223             "com.puppycrawl.tools.checkstyle.checks.coding.IllegalTypeCheck",
224             "com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
225             "com.puppycrawl.tools.checkstyle.checks.coding.MatchXpathCheck",
226             "com.puppycrawl.tools.checkstyle.checks.coding.ModifiedControlVariableCheck",
227             "com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheck",
228             "com.puppycrawl.tools.checkstyle.checks.coding.NestedForDepthCheck",
229             "com.puppycrawl.tools.checkstyle.checks.coding.NestedTryDepthCheck",
230             "com.puppycrawl.tools.checkstyle.checks.coding.StringLiteralEqualityCheck",
231             "com.puppycrawl.tools.checkstyle.checks.coding.SuperFinalizeCheck",
232             "com.puppycrawl.tools.checkstyle.checks.coding"
233                     + ".UnnecessarySemicolonAfterTypeMemberDeclarationCheck",
234             "com.puppycrawl.tools.checkstyle.checks.coding"
235                     + ".UnusedCatchParameterShouldBeUnnamedCheck",
236             "com.puppycrawl.tools.checkstyle.checks.design.DesignForExtensionCheck",
237             "com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck",
238             "com.puppycrawl.tools.checkstyle.checks.design.InnerTypeLastCheck",
239             "com.puppycrawl.tools.checkstyle.checks.design.MutableExceptionCheck",
240             "com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck",
241 
242             "com.puppycrawl.tools.checkstyle.checks.design.VisibilityModifierCheck",
243             "com.puppycrawl.tools.checkstyle.checks.javadoc."
244                     + "AbstractJavadocCheckTest$TokenIsNotInAcceptablesCheck",
245             "com.puppycrawl.tools.checkstyle.checks.javadoc.AtclauseOrderCheck",
246             "com.puppycrawl.tools.checkstyle.checks.javadoc.InvalidJavadocPositionCheck",
247             "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocBlockTagLocationCheck",
248             "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMissingLeadingAsteriskCheck",
249             "com.puppycrawl.tools.checkstyle.checks.javadoc"
250                     + ".JavadocMissingWhitespaceAfterAsteriskCheck",
251             "com.puppycrawl.tools.checkstyle.checks.javadoc"
252                     + ".JavadocTagContinuationIndentationCheck",
253             "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck",
254             "com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck",
255             "com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocPackageCheck",
256             "com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck",
257             "com.puppycrawl.tools.checkstyle.checks.javadoc.NonEmptyAtclauseDescriptionCheck",
258             "com.puppycrawl.tools.checkstyle.checks.javadoc"
259                     + ".RequireEmptyLineBeforeBlockTagGroupCheck",
260             "com.puppycrawl.tools.checkstyle.checks.javadoc.SingleLineJavadocCheck",
261             "com.puppycrawl.tools.checkstyle.checks.metrics.BooleanExpressionComplexityCheck",
262             "com.puppycrawl.tools.checkstyle.checks.metrics.ClassDataAbstractionCouplingCheck",
263             "com.puppycrawl.tools.checkstyle.checks.metrics.ClassFanOutComplexityCheck",
264             "com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheck",
265             "com.puppycrawl.tools.checkstyle.checks.metrics.NPathComplexityCheck",
266             "com.puppycrawl.tools.checkstyle.checks.modifier.ClassMemberImpliedModifierCheck",
267             "com.puppycrawl.tools.checkstyle.checks.modifier.InterfaceMemberImpliedModifierCheck",
268             "com.puppycrawl.tools.checkstyle.checks.modifier.RedundantModifierCheck",
269             "com.puppycrawl.tools.checkstyle.checks.naming.AbbreviationAsWordInNameCheck",
270 
271             "com.puppycrawl.tools.checkstyle.checks.naming.IllegalIdentifierNameCheck",
272             "com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck",
273             "com.puppycrawl.tools.checkstyle.checks.naming.MethodTypeParameterNameCheck",
274             "com.puppycrawl.tools.checkstyle.checks.naming.ParameterNameCheck",
275             "com.puppycrawl.tools.checkstyle.checks.naming.PatternVariableNameCheck",
276             "com.puppycrawl.tools.checkstyle.checks.naming.RecordComponentNameCheck",
277             "com.puppycrawl.tools.checkstyle.checks.naming.RecordTypeParameterNameCheck",
278             "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpMultilineCheck",
279             "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck",
280             "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck",
281             "com.puppycrawl.tools.checkstyle.checks.sizes.AnonInnerLengthCheck",
282             "com.puppycrawl.tools.checkstyle.checks.sizes.ExecutableStatementCountCheck",
283             "com.puppycrawl.tools.checkstyle.checks.sizes.LambdaBodyLengthCheck",
284             "com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck",
285             "com.puppycrawl.tools.checkstyle.checks.sizes.OuterTypeNumberCheck",
286             "com.puppycrawl.tools.checkstyle.checks.sizes.ParameterNumberCheck",
287             "com.puppycrawl.tools.checkstyle.checks.sizes.RecordComponentNumberCheck",
288             "com.puppycrawl.tools.checkstyle.checks.TodoCommentCheck",
289             "com.puppycrawl.tools.checkstyle.checks.TrailingCommentCheck",
290             "com.puppycrawl.tools.checkstyle.checks.whitespace.NoLineWrapCheck",
291             "com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceAfterCheck",
292             "com.puppycrawl.tools.checkstyle.checks.whitespace."
293                     + "NoWhitespaceBeforeCaseDefaultColonCheck",
294             "com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceBeforeCheck",
295             "com.puppycrawl.tools.checkstyle.checks.whitespace.ParenPadCheck",
296             "com.puppycrawl.tools.checkstyle.checks.whitespace.SingleSpaceSeparatorCheck",
297             "com.puppycrawl.tools.checkstyle.meta.JavadocMetadataScraper",
298             "com.puppycrawl.tools.checkstyle.api.AbstractCheckTest$ViolationAstCheck",
299             "com.puppycrawl.tools.checkstyle.CheckerTest$VerifyPositionAfterTabFileSet"
300     );
301 
302     // This is a hack until https://github.com/checkstyle/checkstyle/issues/13845
303     private static final Map<String, String> MODULE_MAPPINGS = new HashMap<>();
304 
305     // -@cs[ExecutableStatementCount] Suppressing due to large module mappings
306     static {
307         MODULE_MAPPINGS.put("IllegalCatch",
308                 "com.puppycrawl.tools.checkstyle.checks.coding.IllegalCatchCheck");
309         MODULE_MAPPINGS.put("MagicNumber",
310                 "com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck");
311         MODULE_MAPPINGS.put("SummaryJavadoc",
312                 "com.puppycrawl.tools.checkstyle.checks.javadoc.SummaryJavadocCheck");
313         MODULE_MAPPINGS.put("ClassDataAbstractionCoupling",
314                 "com.puppycrawl.tools.checkstyle.checks.metrics.ClassDataAbstractionCouplingCheck");
315         MODULE_MAPPINGS.put("ConstantName",
316                 "com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck");
317         MODULE_MAPPINGS.put("MemberName",
318                 "com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck");
319         MODULE_MAPPINGS.put("MethodName",
320                 "com.puppycrawl.tools.checkstyle.checks.naming.MethodNameCheck");
321         MODULE_MAPPINGS.put("ParameterName",
322                 "com.puppycrawl.tools.checkstyle.checks.naming.ParameterNameCheck");
323         MODULE_MAPPINGS.put("RegexpOnFilename",
324                 "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpOnFilenameCheck");
325         MODULE_MAPPINGS.put("RegexpSingleline",
326                 "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck");
327         MODULE_MAPPINGS.put("RegexpSinglelineJava",
328                 "com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck");
329         MODULE_MAPPINGS.put("LineLength",
330                 "com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck");
331         MODULE_MAPPINGS.put("ParameterNumber",
332                 "com.puppycrawl.tools.checkstyle.checks.sizes.ParameterNumberCheck");
333         MODULE_MAPPINGS.put("NoWhitespaceAfter",
334                 "com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceAfterCheck");
335         MODULE_MAPPINGS.put("OrderedProperties",
336                 "com.puppycrawl.tools.checkstyle.checks.OrderedPropertiesCheck");
337         MODULE_MAPPINGS.put("SuppressWarningsHolder",
338                 "com.puppycrawl.tools.checkstyle.checks.SuppressWarningsHolder");
339         MODULE_MAPPINGS.put("UniqueProperties",
340                 "com.puppycrawl.tools.checkstyle.checks.UniquePropertiesCheck");
341         MODULE_MAPPINGS.put("SuppressionXpathSingleFilter",
342                 "com.puppycrawl.tools.checkstyle.filters.SuppressionXpathSingleFilter");
343         MODULE_MAPPINGS.put("SuppressWarningsFilter",
344                 "com.puppycrawl.tools.checkstyle.filters.SuppressWarningsFilter");
345         MODULE_MAPPINGS.put("LeftCurly",
346                 "com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck");
347         MODULE_MAPPINGS.put("RequireThis",
348                 "com.puppycrawl.tools.checkstyle.checks.coding.RequireThisCheck");
349         MODULE_MAPPINGS.put("IllegalThrows",
350                 "com.puppycrawl.tools.checkstyle.checks.coding.IllegalThrowsCheck");
351         MODULE_MAPPINGS.put("LocalFinalVariableName",
352                 "com.puppycrawl.tools.checkstyle.checks.naming.LocalFinalVariableNameCheck");
353         MODULE_MAPPINGS.put("PackageName",
354                 "com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck");
355         MODULE_MAPPINGS.put("RedundantModifier",
356                 "com.puppycrawl.tools.checkstyle.checks.modifier.RedundantModifierCheck");
357         MODULE_MAPPINGS.put("AbstractClassName",
358                 "com.puppycrawl.tools.checkstyle.checks.naming.AbstractClassNameCheck");
359         MODULE_MAPPINGS.put("JavadocMethod",
360                 "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck");
361         MODULE_MAPPINGS.put("IllegalIdentifierName",
362                 "com.puppycrawl.tools.checkstyle.checks.naming.IllegalIdentifierNameCheck");
363         MODULE_MAPPINGS.put("FileLength",
364                 "com.puppycrawl.tools.checkstyle.checks.sizes.FileLengthCheck");
365         MODULE_MAPPINGS.put("EqualsAvoidNull",
366                 "com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck");
367         MODULE_MAPPINGS.put("JavadocStyle",
368                 "com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocStyleCheck");
369     }
370 
371     /** Stop instances being created. **/
372     private InlineConfigParser() {
373     }
374 
375     public static TestInputConfiguration parse(String inputFilePath) throws Exception {
376         return parse(inputFilePath, false);
377     }
378 
379     /**
380      * Parses the input file provided.
381      *
382      * @param inputFilePath the input file path.
383      * @param setFilteredViolations flag to set filtered violations.
384      * @throws Exception if unable to read file or file not formatted properly.
385      */
386     private static TestInputConfiguration parse(String inputFilePath,
387                                                 boolean setFilteredViolations) throws Exception {
388         final TestInputConfiguration.Builder testInputConfigBuilder =
389                 new TestInputConfiguration.Builder();
390         final Path filePath = Path.of(inputFilePath);
391         final List<String> lines = readFile(filePath);
392         try {
393             setModules(testInputConfigBuilder, inputFilePath, lines);
394         }
395         catch (Exception ex) {
396             throw new CheckstyleException("Config comment not specified properly in "
397                     + inputFilePath, ex);
398         }
399         try {
400             setViolations(testInputConfigBuilder, lines, setFilteredViolations);
401         }
402         catch (CheckstyleException ex) {
403             throw new CheckstyleException(ex.getMessage() + " in " + inputFilePath, ex);
404         }
405         return testInputConfigBuilder.build();
406     }
407 
408     public static List<TestInputViolation> getViolationsFromInputFile(String inputFilePath)
409             throws Exception {
410         final TestInputConfiguration.Builder testInputConfigBuilder =
411                 new TestInputConfiguration.Builder();
412         final Path filePath = Path.of(inputFilePath);
413         final List<String> lines = readFile(filePath);
414 
415         try {
416             for (int lineNo = 0; lineNo < lines.size(); lineNo++) {
417                 setViolations(testInputConfigBuilder, lines, false, lineNo, true);
418             }
419         }
420         catch (CheckstyleException ex) {
421             throw new CheckstyleException(ex.getMessage() + " in " + inputFilePath, ex);
422         }
423 
424         return testInputConfigBuilder.build().getViolations();
425     }
426 
427     public static TestInputConfiguration parseWithFilteredViolations(String inputFilePath)
428             throws Exception {
429         return parse(inputFilePath, true);
430     }
431 
432     /**
433      * Parse the input file with configuration in xml header.
434      *
435      * @param inputFilePath the input file path.
436      * @throws Exception if unable to parse the xml header
437      */
438     public static TestInputConfiguration parseWithXmlHeader(String inputFilePath)
439             throws Exception {
440 
441         final Path filePath = Path.of(inputFilePath);
442         final List<String> lines = readFile(filePath);
443         if (!checkIsXmlConfig(lines)) {
444             throw new CheckstyleException("Config cannot be parsed as xml.");
445         }
446 
447         final List<String> inlineConfig = getInlineConfig(lines);
448         final String stringXmlConfig = LATEST_DTD + String.join("", inlineConfig);
449         final InputSource inputSource = new InputSource(new StringReader(stringXmlConfig));
450         final Configuration xmlConfig = ConfigurationLoader.loadConfiguration(
451                 inputSource, new PropertiesExpander(System.getProperties()),
452                 ConfigurationLoader.IgnoredModulesOptions.EXECUTE
453         );
454         final String configName = xmlConfig.getName();
455         if (!"Checker".equals(configName)) {
456             throw new CheckstyleException(
457                     "First module should be Checker, but was " + configName);
458         }
459 
460         final TestInputConfiguration.Builder testInputConfigBuilder =
461                 new TestInputConfiguration.Builder();
462         testInputConfigBuilder.setXmlConfiguration(xmlConfig);
463         try {
464             setViolations(testInputConfigBuilder, lines, false);
465         }
466         catch (CheckstyleException ex) {
467             throw new CheckstyleException(ex.getMessage() + " in " + inputFilePath, ex);
468         }
469         return testInputConfigBuilder.buildWithXmlConfiguration();
470     }
471 
472     /**
473      * Check whether a file provides xml configuration.
474      *
475      * @param lines lines of the file
476      * @return true if a file provides xml configuration, otherwise false.
477      */
478     private static boolean checkIsXmlConfig(List<String> lines) {
479         return "/*xml".equals(lines.get(0));
480     }
481 
482     private static void setModules(TestInputConfiguration.Builder testInputConfigBuilder,
483                                    String inputFilePath, List<String> lines)
484             throws Exception {
485         if (!lines.get(0).startsWith("/*")) {
486             throw new CheckstyleException("Config not specified on top."
487                 + "Please see other inputs for examples of what is required.");
488         }
489 
490         final List<String> inlineConfig = getInlineConfig(lines);
491 
492         if (checkIsXmlConfig(lines)) {
493             final String stringXmlConfig = LATEST_DTD + String.join("", inlineConfig);
494             final InputSource inputSource = new InputSource(new StringReader(stringXmlConfig));
495             final Configuration xmlConfig = ConfigurationLoader.loadConfiguration(
496                 inputSource, new PropertiesExpander(System.getProperties()),
497                     ConfigurationLoader.IgnoredModulesOptions.EXECUTE
498             );
499             final String configName = xmlConfig.getName();
500             if (!"Checker".equals(configName)) {
501                 throw new CheckstyleException(
502                         "First module should be Checker, but was " + configName);
503             }
504             handleXmlConfig(testInputConfigBuilder, inputFilePath, xmlConfig.getChildren());
505         }
506         else {
507             handleKeyValueConfig(testInputConfigBuilder, inputFilePath, inlineConfig);
508         }
509     }
510 
511     private static List<String> getInlineConfig(List<String> lines) {
512         return lines.stream()
513                 .skip(1)
514                 .takeWhile(line -> !line.startsWith("*/"))
515                 .collect(Collectors.toUnmodifiableList());
516     }
517 
518     private static void handleXmlConfig(TestInputConfiguration.Builder testInputConfigBuilder,
519                                         String inputFilePath,
520                                         Configuration... modules)
521             throws CheckstyleException {
522 
523         for (Configuration module: modules) {
524             final String moduleName = module.getName();
525             if ("TreeWalker".equals(moduleName)) {
526                 handleXmlConfig(testInputConfigBuilder, inputFilePath, module.getChildren());
527             }
528             else {
529                 final ModuleInputConfiguration.Builder moduleInputConfigBuilder =
530                         new ModuleInputConfiguration.Builder();
531                 setModuleName(moduleInputConfigBuilder, inputFilePath, moduleName);
532                 setProperties(inputFilePath, module, moduleInputConfigBuilder);
533                 testInputConfigBuilder.addChildModule(moduleInputConfigBuilder.build());
534             }
535         }
536     }
537 
538     private static void handleKeyValueConfig(TestInputConfiguration.Builder testInputConfigBuilder,
539                                              String inputFilePath, List<String> lines)
540             throws CheckstyleException, IOException, ReflectiveOperationException {
541         int lineNo = 0;
542         while (lineNo < lines.size()) {
543             final ModuleInputConfiguration.Builder moduleInputConfigBuilder =
544                     new ModuleInputConfiguration.Builder();
545             final String moduleName = lines.get(lineNo);
546             setModuleName(moduleInputConfigBuilder, inputFilePath, moduleName);
547             setProperties(moduleInputConfigBuilder, inputFilePath, lines, lineNo + 1, moduleName);
548             testInputConfigBuilder.addChildModule(moduleInputConfigBuilder.build());
549             do {
550                 lineNo++;
551             } while (lineNo < lines.size()
552                     && lines.get(lineNo).isEmpty()
553                     || !lines.get(lineNo - 1).isEmpty());
554         }
555     }
556 
557     private static String getFullyQualifiedClassName(String filePath, String moduleName)
558             throws CheckstyleException {
559         String fullyQualifiedClassName;
560         if (MODULE_MAPPINGS.containsKey(moduleName)) {
561             fullyQualifiedClassName = MODULE_MAPPINGS.get(moduleName);
562         }
563         else if (moduleName.startsWith("com.")) {
564             fullyQualifiedClassName = moduleName;
565         }
566         else {
567             final String path = SLASH_PATTERN.matcher(filePath).replaceAll(".");
568             final int endIndex = path.lastIndexOf(moduleName.toLowerCase(Locale.ROOT));
569             if (endIndex == -1) {
570                 throw new CheckstyleException("Unable to resolve module name: " + moduleName
571                 + ". Please check for spelling errors or specify fully qualified class name.");
572             }
573             final int beginIndex = path.indexOf("com.puppycrawl");
574             fullyQualifiedClassName = path.substring(beginIndex, endIndex) + moduleName;
575             if (!fullyQualifiedClassName.endsWith("Filter")) {
576                 fullyQualifiedClassName += "Check";
577             }
578         }
579         return fullyQualifiedClassName;
580     }
581 
582     private static String getFilePath(String fileName, String inputFilePath) {
583         final int lastSlashIndex = Math.max(inputFilePath.lastIndexOf('\\'),
584                 inputFilePath.lastIndexOf('/'));
585         final String root = inputFilePath.substring(0, lastSlashIndex + 1);
586         return root + fileName;
587     }
588 
589     private static String getResourcePath(String fileName, String inputFilePath) {
590         final String filePath = getUriPath(fileName, inputFilePath);
591         final int lastSlashIndex = filePath.lastIndexOf('/');
592         final String root = filePath.substring(filePath.indexOf("puppycrawl") - 5,
593                 lastSlashIndex + 1);
594         return root + fileName;
595     }
596 
597     private static String getUriPath(String fileName, String inputFilePath) {
598         return new File(getFilePath(fileName, inputFilePath)).toURI().toString();
599     }
600 
601     private static String getResolvedPath(String fileValue, String inputFilePath) {
602         final String resolvedFilePath;
603         if (fileValue.startsWith("(resource)")) {
604             resolvedFilePath =
605                     getResourcePath(fileValue.substring(fileValue.indexOf(')') + 1),
606                             inputFilePath);
607         }
608         else if (fileValue.startsWith("(uri)")) {
609             resolvedFilePath =
610                     getUriPath(fileValue.substring(fileValue.indexOf(')') + 1), inputFilePath);
611         }
612         else {
613             resolvedFilePath = getFilePath(fileValue, inputFilePath);
614         }
615         return resolvedFilePath;
616     }
617 
618     private static List<String> readFile(Path filePath) throws CheckstyleException {
619         try {
620             return Files.readAllLines(filePath);
621         }
622         catch (IOException ex) {
623             throw new CheckstyleException("Failed to read " + filePath, ex);
624         }
625     }
626 
627     private static void setModuleName(ModuleInputConfiguration.Builder moduleInputConfigBuilder,
628                                       String filePath, String moduleName)
629             throws CheckstyleException {
630         final String fullyQualifiedClassName = getFullyQualifiedClassName(filePath, moduleName);
631         moduleInputConfigBuilder.setModuleName(fullyQualifiedClassName);
632     }
633 
634     private static String toStringConvertForArrayValue(Object value) {
635         String result = NULL_STRING;
636 
637         if (value instanceof double[]) {
638             final double[] arr = (double[]) value;
639             result = Arrays.stream(arr)
640                            .boxed()
641                            .map(number -> {
642                                return BigDecimal.valueOf(number)
643                                                 .stripTrailingZeros()
644                                                 .toPlainString();
645                            })
646                            .collect(Collectors.joining(","));
647         }
648         else if (value instanceof int[]) {
649             result = Arrays.toString((int[]) value).replaceAll("[\\[\\]\\s]", "");
650         }
651         else if (value instanceof boolean[]) {
652             result = Arrays.toString((boolean[]) value).replaceAll("[\\[\\]\\s]", "");
653         }
654         else if (value instanceof long[]) {
655             result = Arrays.toString((long[]) value).replaceAll("[\\[\\]\\s]", "");
656         }
657         else if (value instanceof Object[]) {
658             result = Arrays.toString((Object[]) value).replaceAll("[\\[\\]\\s]", "");
659         }
660         return result;
661     }
662 
663     /**
664      * Validate default value.
665      *
666      * @param propertyName the property name.
667      * @param propertyDefaultValue the specified default value in the file.
668      * @param fullyQualifiedModuleName the fully qualified module name.
669      */
670     private static void validateDefault(String propertyName,
671                                            String propertyDefaultValue,
672                                            String fullyQualifiedModuleName)
673             throws ReflectiveOperationException {
674         final Object checkInstance = createCheckInstance(fullyQualifiedModuleName);
675         final Object actualDefault;
676         final Class<?> propertyType;
677         final String actualDefaultAsString;
678 
679         if ("tokens".equals(propertyName)) {
680             final Method getter = checkInstance.getClass().getMethod("getDefaultTokens");
681             actualDefault = getter.invoke(checkInstance);
682             propertyType = actualDefault.getClass();
683             final int[] arr = (int[]) actualDefault;
684             actualDefaultAsString = Arrays.stream(arr)
685                                           .mapToObj(TokenUtil::getTokenName)
686                                           .collect(Collectors.joining(", "));
687         }
688         else if ("javadocTokens".equals(propertyName)) {
689             final Method getter = checkInstance.getClass().getMethod("getDefaultJavadocTokens");
690             actualDefault = getter.invoke(checkInstance);
691             propertyType = actualDefault.getClass();
692             final int[] arr = (int[]) actualDefault;
693             actualDefaultAsString = Arrays.stream(arr)
694                                           .mapToObj(JavadocUtil::getTokenName)
695                                           .collect(Collectors.joining(", "));
696         }
697         else {
698             actualDefault = getPropertyDefaultValue(checkInstance, propertyName);
699             if (actualDefault == null) {
700                 propertyType = null;
701             }
702             else {
703                 propertyType = actualDefault.getClass();
704             }
705             actualDefaultAsString = convertDefaultValueToString(actualDefault);
706         }
707         if (!isDefaultValue(propertyDefaultValue, actualDefaultAsString, propertyType)) {
708             final String message = String.format(Locale.ROOT,
709                     "Default value mismatch for %s in %s: specified '%s' but actually is '%s'",
710                     propertyName, fullyQualifiedModuleName,
711                     propertyDefaultValue, actualDefaultAsString);
712             throw new IllegalArgumentException(message);
713         }
714     }
715 
716     private static boolean isCollectionValues(String specifiedDefault, String actualDefault) {
717         final Set<String> specifiedSet = new HashSet<>(
718             Arrays.asList(specifiedDefault.replaceAll("[\\[\\]\\s]", "").split(",")));
719         final Set<String> actualSet = new HashSet<>(
720             Arrays.asList(actualDefault.replaceAll("[\\[\\]\\s]", "").split(",")));
721         return actualSet.containsAll(specifiedSet);
722     }
723 
724     private static String convertDefaultValueToString(Object value) {
725         final String defaultValueAsString;
726         if (value == null) {
727             defaultValueAsString = NULL_STRING;
728         }
729         else if (value instanceof String) {
730             defaultValueAsString = toStringForStringValue((String) value);
731         }
732         else if (value.getClass().isArray()) {
733             defaultValueAsString = toStringConvertForArrayValue(value);
734         }
735         else if (value instanceof BitSet) {
736             defaultValueAsString = toStringForBitSetValue((BitSet) value);
737         }
738         else if (value instanceof Collection<?>) {
739             defaultValueAsString = toStringForCollectionValue((Collection<?>) value);
740         }
741         else {
742             defaultValueAsString = String.valueOf(value);
743         }
744         return defaultValueAsString;
745     }
746 
747     private static String toStringForStringValue(String strValue) {
748         final String str;
749         if (strValue.startsWith("(") && strValue.endsWith(")")) {
750             str = strValue.substring(1, strValue.length() - 1);
751         }
752         else {
753             str = strValue;
754         }
755         return str;
756     }
757 
758     private static String toStringForBitSetValue(BitSet bitSet) {
759         return bitSet.stream()
760                      .mapToObj(TokenUtil::getTokenName)
761                      .collect(Collectors.joining(","));
762     }
763 
764     private static String toStringForCollectionValue(Collection<?> collection) {
765         return collection.toString().replaceAll("[\\[\\]\\s]", "");
766     }
767 
768     /**
769      * Validate default value.
770      *
771      * @param propertyDefaultValue the specified default value in the file.
772      * @param actualDefault the actual default value
773      * @param fieldType the data type of default value.
774      */
775     private static boolean isDefaultValue(final String propertyDefaultValue,
776                                           final String actualDefault,
777                                           final Class<?> fieldType) {
778         final boolean result;
779 
780         if (NULL_STRING.equals(actualDefault)) {
781             result = isNull(propertyDefaultValue);
782         }
783         else if (isNumericType(fieldType)) {
784             final BigDecimal specified = new BigDecimal(propertyDefaultValue);
785             final BigDecimal actual = new BigDecimal(actualDefault);
786             result = specified.compareTo(actual) == 0;
787         }
788         else if (fieldType.isArray()
789             || Collection.class.isAssignableFrom(fieldType)
790             || BitSet.class.isAssignableFrom(fieldType)) {
791             result = isCollectionValues(propertyDefaultValue, actualDefault);
792         }
793         else if (fieldType.isEnum() || fieldType.isLocalClass()) {
794             result = propertyDefaultValue.equalsIgnoreCase(actualDefault);
795         }
796         else {
797             result = propertyDefaultValue.equals(actualDefault);
798         }
799         return result;
800     }
801 
802     private static Object createCheckInstance(String className) throws
803             ReflectiveOperationException {
804         final Class<?> checkClass = Class.forName(className);
805         return checkClass.getDeclaredConstructor().newInstance();
806     }
807 
808     private static String readPropertiesContent(int beginLineNo, List<String> lines) {
809         final StringBuilder stringBuilder = new StringBuilder(128);
810         int lineNo = beginLineNo;
811         String line = lines.get(lineNo);
812         while (!line.isEmpty() && !"*/".equals(line)) {
813             stringBuilder.append(line).append('\n');
814             lineNo++;
815             line = lines.get(lineNo);
816         }
817         return stringBuilder.toString();
818     }
819 
820     private static void setProperties(ModuleInputConfiguration.Builder inputConfigBuilder,
821                             String inputFilePath,
822                             List<String> lines,
823                             int beginLineNo, String moduleName)
824             throws IOException, CheckstyleException, ReflectiveOperationException {
825 
826         final String propertyContent = readPropertiesContent(beginLineNo, lines);
827         final Map<Object, Object> properties = loadProperties(propertyContent);
828 
829         for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
830             final String key = entry.getKey().toString();
831             final String value = entry.getValue().toString();
832 
833             if (key.startsWith("message.")) {
834                 inputConfigBuilder.addModuleMessage(key.substring(8), value);
835             }
836             else if (NULL_STRING.equals(value)) {
837                 inputConfigBuilder.addNonDefaultProperty(key, null);
838             }
839             else if (value.startsWith("(file)")) {
840                 final String fileName = value.substring(value.indexOf(')') + 1);
841                 final String filePath = getResolvedPath(fileName, inputFilePath);
842                 inputConfigBuilder.addNonDefaultProperty(key, filePath);
843             }
844             else if (value.startsWith("(default)")) {
845                 final String defaultValue = value.substring(value.indexOf(')') + 1);
846                 final String fullyQualifiedModuleName =
847                         getFullyQualifiedClassName(inputFilePath, moduleName);
848 
849                 validateDefault(key, defaultValue, fullyQualifiedModuleName);
850 
851                 if (NULL_STRING.equals(defaultValue)) {
852                     inputConfigBuilder.addDefaultProperty(key, null);
853                 }
854                 else {
855                     inputConfigBuilder.addDefaultProperty(key, defaultValue);
856                 }
857             }
858             else {
859                 inputConfigBuilder.addNonDefaultProperty(key, value);
860             }
861         }
862     }
863 
864     private static void setProperties(String inputFilePath, Configuration module,
865                                       ModuleInputConfiguration.Builder moduleInputConfigBuilder)
866             throws CheckstyleException {
867         final String[] getPropertyNames = module.getPropertyNames();
868         for (final String propertyName : getPropertyNames) {
869             final String propertyValue = module.getProperty(propertyName);
870 
871             if ("file".equals(propertyName)) {
872                 final String filePath = getResolvedPath(propertyValue, inputFilePath);
873                 moduleInputConfigBuilder.addNonDefaultProperty(propertyName, filePath);
874             }
875             else {
876                 if (NULL_STRING.equals(propertyValue)) {
877                     moduleInputConfigBuilder.addNonDefaultProperty(propertyName, null);
878                 }
879                 else {
880                     moduleInputConfigBuilder.addNonDefaultProperty(propertyName, propertyValue);
881                 }
882             }
883         }
884 
885         final Map<String, String> messages = module.getMessages();
886         for (final Map.Entry<String, String> entry : messages.entrySet()) {
887             final String key = entry.getKey();
888             final String value = entry.getValue();
889             moduleInputConfigBuilder.addModuleMessage(key, value);
890         }
891     }
892 
893     private static void setViolations(TestInputConfiguration.Builder inputConfigBuilder,
894                                       List<String> lines, boolean useFilteredViolations)
895             throws CheckstyleException {
896         final List<ModuleInputConfiguration> moduleLists = inputConfigBuilder.getChildrenModules();
897         final boolean specifyViolationMessage = moduleLists.size() == 1
898                 && !PERMANENT_SUPPRESSED_CHECKS.contains(moduleLists.get(0).getModuleName())
899                 && !SUPPRESSED_CHECKS.contains(moduleLists.get(0).getModuleName());
900         for (int lineNo = 0; lineNo < lines.size(); lineNo++) {
901             setViolations(inputConfigBuilder, lines,
902                     useFilteredViolations, lineNo, specifyViolationMessage);
903         }
904     }
905 
906     /**
907      * Sets the violations.
908      *
909      * @param inputConfigBuilder the input file path.
910      * @param lines all the lines in the file.
911      * @param useFilteredViolations flag to set filtered violations.
912      * @param lineNo current line.
913      * @noinspection IfStatementWithTooManyBranches
914      * @noinspectionreason IfStatementWithTooManyBranches - complex logic of violation
915      *      parser requires giant if/else
916      * @throws CheckstyleException if violation message is not specified
917      */
918     // -@cs[ExecutableStatementCount] splitting this method is not reasonable.
919     // -@cs[JavaNCSS] splitting this method is not reasonable.
920     // -@cs[CyclomaticComplexity] splitting this method is not reasonable.
921     private static void setViolations(TestInputConfiguration.Builder inputConfigBuilder,
922                                       List<String> lines, boolean useFilteredViolations,
923                                       int lineNo, boolean specifyViolationMessage)
924             throws CheckstyleException {
925         final Matcher violationMatcher =
926                 VIOLATION_PATTERN.matcher(lines.get(lineNo));
927         final Matcher violationAboveMatcher =
928                 VIOLATION_ABOVE_PATTERN.matcher(lines.get(lineNo));
929         final Matcher violationBelowMatcher =
930                 VIOLATION_BELOW_PATTERN.matcher(lines.get(lineNo));
931         final Matcher violationAboveWithExplanationMatcher =
932                 VIOLATION_ABOVE_WITH_EXPLANATION_PATTERN.matcher(lines.get(lineNo));
933         final Matcher violationBelowWithExplanationMatcher =
934                 VIOLATION_BELOW_WITH_EXPLANATION_PATTERN.matcher(lines.get(lineNo));
935         final Matcher violationWithExplanationMatcher =
936                 VIOLATION_WITH_EXPLANATION_PATTERN.matcher(lines.get(lineNo));
937         final Matcher multipleViolationsMatcher =
938                 MULTIPLE_VIOLATIONS_PATTERN.matcher(lines.get(lineNo));
939         final Matcher multipleViolationsAboveMatcher =
940                 MULTIPLE_VIOLATIONS_ABOVE_PATTERN.matcher(lines.get(lineNo));
941         final Matcher multipleViolationsBelowMatcher =
942                 MULTIPLE_VIOLATIONS_BELOW_PATTERN.matcher(lines.get(lineNo));
943         final Matcher violationSomeLinesAboveMatcher =
944                 VIOLATION_SOME_LINES_ABOVE_PATTERN.matcher(lines.get(lineNo));
945         final Matcher violationSomeLinesBelowMatcher =
946                 VIOLATION_SOME_LINES_BELOW_PATTERN.matcher(lines.get(lineNo));
947         final Matcher violationsAboveMatcherWithMessages =
948                 VIOLATIONS_ABOVE_PATTERN_WITH_MESSAGES.matcher(lines.get(lineNo));
949         final Matcher violationsSomeLinesAboveMatcher =
950                 VIOLATIONS_SOME_LINES_ABOVE_PATTERN.matcher(lines.get(lineNo));
951         final Matcher violationsSomeLinesBelowMatcher =
952                 VIOLATIONS_SOME_LINES_BELOW_PATTERN.matcher(lines.get(lineNo));
953         final Matcher violationsDefault =
954                 VIOLATION_DEFAULT.matcher(lines.get(lineNo));
955         if (violationMatcher.matches()) {
956             final String violationMessage = violationMatcher.group(1);
957             final int violationLineNum = lineNo + 1;
958             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
959                     violationLineNum);
960             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
961         }
962         else if (violationAboveMatcher.matches()) {
963             final String violationMessage = violationAboveMatcher.group(1);
964             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage, lineNo);
965             inputConfigBuilder.addViolation(lineNo, violationMessage);
966         }
967         else if (violationBelowMatcher.matches()) {
968             final String violationMessage = violationBelowMatcher.group(1);
969             final int violationLineNum = lineNo + 2;
970             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
971                     violationLineNum);
972             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
973         }
974         else if (violationAboveWithExplanationMatcher.matches()) {
975             final String violationMessage = violationAboveWithExplanationMatcher.group(1);
976             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage, lineNo);
977             inputConfigBuilder.addViolation(lineNo, violationMessage);
978         }
979         else if (violationBelowWithExplanationMatcher.matches()) {
980             final String violationMessage = violationBelowWithExplanationMatcher.group(1);
981             final int violationLineNum = lineNo + 2;
982             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
983                     violationLineNum);
984             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
985         }
986         else if (violationWithExplanationMatcher.matches()) {
987             final int violationLineNum = lineNo + 1;
988             inputConfigBuilder.addViolation(violationLineNum, null);
989         }
990         else if (violationSomeLinesAboveMatcher.matches()) {
991             final String violationMessage = violationSomeLinesAboveMatcher.group(2);
992             final int linesAbove = Integer.parseInt(violationSomeLinesAboveMatcher.group(1)) - 1;
993             final int violationLineNum = lineNo - linesAbove;
994             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
995                     violationLineNum);
996             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
997         }
998         else if (violationSomeLinesBelowMatcher.matches()) {
999             final String violationMessage = violationSomeLinesBelowMatcher.group(2);
1000             final int linesBelow = Integer.parseInt(violationSomeLinesBelowMatcher.group(1)) + 1;
1001             final int violationLineNum = lineNo + linesBelow;
1002             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
1003                     violationLineNum);
1004             inputConfigBuilder.addViolation(violationLineNum, violationMessage);
1005         }
1006         else if (violationsAboveMatcherWithMessages.matches()) {
1007             inputConfigBuilder.addViolations(
1008                 getExpectedViolationsForSpecificLine(
1009                     lines, lineNo, lineNo, violationsAboveMatcherWithMessages));
1010         }
1011         else if (violationsSomeLinesAboveMatcher.matches()) {
1012             inputConfigBuilder.addViolations(
1013                 getExpectedViolations(
1014                     lines, lineNo, violationsSomeLinesAboveMatcher, true));
1015         }
1016         else if (violationsSomeLinesBelowMatcher.matches()) {
1017             inputConfigBuilder.addViolations(
1018                     getExpectedViolations(
1019                             lines, lineNo, violationsSomeLinesBelowMatcher, false));
1020         }
1021         else if (multipleViolationsMatcher.matches()) {
1022             Collections
1023                     .nCopies(Integer.parseInt(multipleViolationsMatcher.group(1)), lineNo + 1)
1024                     .forEach(actualLineNumber -> {
1025                         inputConfigBuilder.addViolation(actualLineNumber, null);
1026                     });
1027         }
1028         else if (multipleViolationsAboveMatcher.matches()) {
1029             Collections
1030                     .nCopies(Integer.parseInt(multipleViolationsAboveMatcher.group(1)), lineNo)
1031                     .forEach(actualLineNumber -> {
1032                         inputConfigBuilder.addViolation(actualLineNumber, null);
1033                     });
1034         }
1035         else if (multipleViolationsBelowMatcher.matches()) {
1036             Collections
1037                     .nCopies(Integer.parseInt(multipleViolationsBelowMatcher.group(1)),
1038                             lineNo + 2)
1039                     .forEach(actualLineNumber -> {
1040                         inputConfigBuilder.addViolation(actualLineNumber, null);
1041                     });
1042         }
1043         else if (useFilteredViolations) {
1044             setFilteredViolation(inputConfigBuilder, lineNo + 1,
1045                     lines.get(lineNo), specifyViolationMessage);
1046         }
1047         else if (violationsDefault.matches()) {
1048             final int violationLineNum = lineNo + 1;
1049             inputConfigBuilder.addViolation(violationLineNum, null);
1050         }
1051     }
1052 
1053     private static List<TestInputViolation> getExpectedViolationsForSpecificLine(
1054                                               List<String> lines, int lineNo, int violationLineNum,
1055                                               Matcher matcher) {
1056         final List<TestInputViolation> results = new ArrayList<>();
1057 
1058         final int expectedMessageCount =
1059             Integer.parseInt(matcher.group(1));
1060         for (int index = 1; index <= expectedMessageCount; index++) {
1061             final String lineWithMessage = lines.get(lineNo + index);
1062             final Matcher messageMatcher = VIOLATION_MESSAGE_PATTERN.matcher(lineWithMessage);
1063             if (messageMatcher.find()) {
1064                 final String violationMessage = messageMatcher.group(1);
1065                 results.add(new TestInputViolation(violationLineNum, violationMessage));
1066             }
1067         }
1068         if (results.size() != expectedMessageCount) {
1069             final String message = String.format(Locale.ROOT,
1070                 "Declared amount of violation messages at line %s is %s but found %s",
1071                 lineNo + 1, expectedMessageCount, results.size());
1072             throw new IllegalStateException(message);
1073         }
1074         return results;
1075     }
1076 
1077     private static List<TestInputViolation> getExpectedViolations(
1078                                               List<String> lines, int lineNo,
1079                                               Matcher matcher, boolean isAbove) {
1080         final int violationLine =
1081             Integer.parseInt(matcher.group(2));
1082         final int violationLineNum;
1083         if (isAbove) {
1084             violationLineNum = lineNo - violationLine + 1;
1085         }
1086         else {
1087             violationLineNum = lineNo + violationLine + 1;
1088         }
1089         return getExpectedViolationsForSpecificLine(lines,
1090             lineNo, violationLineNum, matcher);
1091     }
1092 
1093     private static void setFilteredViolation(TestInputConfiguration.Builder inputConfigBuilder,
1094                                              int lineNo, String line,
1095                                              boolean specifyViolationMessage)
1096             throws CheckstyleException {
1097         final Matcher violationMatcher =
1098                 FILTERED_VIOLATION_PATTERN.matcher(line);
1099         final Matcher violationAboveMatcher =
1100                 FILTERED_VIOLATION_ABOVE_PATTERN.matcher(line);
1101         final Matcher violationBelowMatcher =
1102                 FILTERED_VIOLATION_BELOW_PATTERN.matcher(line);
1103         final Matcher violationSomeLinesAboveMatcher =
1104                 FILTERED_VIOLATION_SOME_LINES_ABOVE_PATTERN.matcher(line);
1105         if (violationMatcher.matches()) {
1106             final String violationMessage = violationMatcher.group(1);
1107             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage, lineNo);
1108             inputConfigBuilder.addFilteredViolation(lineNo, violationMessage);
1109         }
1110         else if (violationAboveMatcher.matches()) {
1111             final String violationMessage = violationAboveMatcher.group(1);
1112             final int violationLineNum = lineNo - 1;
1113             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
1114                     violationLineNum);
1115             inputConfigBuilder.addFilteredViolation(violationLineNum, violationMessage);
1116         }
1117         else if (violationBelowMatcher.matches()) {
1118             final String violationMessage = violationBelowMatcher.group(1);
1119             final int violationLineNum = lineNo + 1;
1120             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
1121                     violationLineNum);
1122             inputConfigBuilder.addFilteredViolation(violationLineNum, violationMessage);
1123         }
1124         else if (violationSomeLinesAboveMatcher.matches()) {
1125             final String violationMessage = violationSomeLinesAboveMatcher.group(2);
1126             final int linesAbove = Integer.parseInt(violationSomeLinesAboveMatcher.group(1)) - 1;
1127             final int violationLineNum = lineNo - linesAbove;
1128             checkWhetherViolationSpecified(specifyViolationMessage, violationMessage,
1129                     violationLineNum);
1130             inputConfigBuilder.addFilteredViolation(violationLineNum, violationMessage);
1131         }
1132     }
1133 
1134     /**
1135      * Check whether violation is specified along with {@code // violation} comment.
1136      *
1137      * @param shouldViolationMsgBeSpecified should violation messages be specified.
1138      * @param violationMessage violation message
1139      * @param lineNum line number
1140      * @throws CheckstyleException if violation message is not specified
1141      */
1142     private static void checkWhetherViolationSpecified(boolean shouldViolationMsgBeSpecified,
1143             String violationMessage, int lineNum) throws CheckstyleException {
1144         if (shouldViolationMsgBeSpecified && violationMessage == null) {
1145             throw new CheckstyleException(
1146                     "Violation message should be specified on line " + lineNum);
1147         }
1148     }
1149 
1150     private static Map<Object, Object> loadProperties(String propertyContent) throws IOException {
1151         final Properties properties = new Properties();
1152         properties.load(new StringReader(propertyContent));
1153         return properties;
1154     }
1155 
1156     private static boolean isNumericType(Class<?> fieldType) {
1157         return Number.class.isAssignableFrom(fieldType)
1158                 || fieldType.equals(int.class)
1159                 || fieldType.equals(double.class)
1160                 || fieldType.equals(long.class)
1161                 || fieldType.equals(float.class);
1162     }
1163 
1164     public static Object getPropertyDefaultValue(Object checkInstance,
1165                                                  String propertyName)
1166             throws IllegalAccessException {
1167         Object result = null;
1168         Class<?> currentClass = checkInstance.getClass();
1169         while (currentClass != null) {
1170             try {
1171                 final Field field = currentClass.getDeclaredField(propertyName);
1172                 field.setAccessible(true);
1173                 result = field.get(checkInstance);
1174                 break;
1175             }
1176             catch (NoSuchFieldException ex) {
1177                 currentClass = currentClass.getSuperclass();
1178             }
1179         }
1180         return result;
1181     }
1182 
1183     private static boolean isNull(String propertyDefaultValue) {
1184         return NULL_STRING.equals(propertyDefaultValue)
1185                 || propertyDefaultValue.isEmpty()
1186                 || "null".equals(propertyDefaultValue)
1187                 || "\"\"".equals(propertyDefaultValue);
1188     }
1189 }