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