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