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