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