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