Skip to content

Commit dc0b652

Browse files
committed
Issue #122: Support multiple instances of the same Checkstyle check
1 parent a0ae57d commit dc0b652

File tree

12 files changed

+112
-40
lines changed

12 files changed

+112
-40
lines changed

src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public List<Recipe> getRecipeList() {
8888
final ReportParser reportParser = createReportParser(getViolationReportPath());
8989
final List<CheckstyleViolation> violations = reportParser
9090
.parse(Path.of(getViolationReportPath()));
91-
final Map<CheckstyleCheck,
91+
final Map<CheckstyleConfigModule,
9292
CheckConfiguration> configuration = loadCheckstyleConfiguration();
9393

9494
return CheckstyleRecipeRegistry.getRecipes(violations, configuration);
@@ -108,7 +108,7 @@ else if (path.endsWith(".sarif") || path.endsWith(".sarif.json")) {
108108
return result;
109109
}
110110

111-
private Map<CheckstyleCheck, CheckConfiguration> loadCheckstyleConfiguration() {
111+
private Map<CheckstyleConfigModule, CheckConfiguration> loadCheckstyleConfiguration() {
112112
return ConfigurationLoader.loadConfiguration(getConfigurationPath(), getPropertiesPath());
113113
}
114114
}

src/main/java/org/checkstyle/autofix/CheckstyleCheck.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,10 @@ public static Optional<CheckstyleCheck> fromSource(String source) {
4242
.filter(check -> check.getId().contains(source))
4343
.findFirst();
4444
}
45+
46+
public static Optional<CheckstyleCheck> fromSourceExact(String source) {
47+
return Arrays.stream(values())
48+
.filter(check -> check.getId().equals(source))
49+
.findFirst();
50+
}
4551
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
///////////////////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite.
3+
// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
///////////////////////////////////////////////////////////////////////////////////////////////
17+
18+
package org.checkstyle.autofix;
19+
20+
public class CheckstyleConfigModule {
21+
private final CheckstyleCheck check;
22+
private final String id;
23+
24+
public CheckstyleConfigModule(CheckstyleCheck check, String id) {
25+
this.check = check;
26+
this.id = id;
27+
}
28+
29+
public boolean matchesId(String input) {
30+
return id != null && id.equals(input);
31+
}
32+
33+
public boolean matchesCheck(String input) {
34+
return CheckstyleCheck.fromSourceExact(input)
35+
.map(checkFromInput -> checkFromInput == check)
36+
.orElse(false);
37+
}
38+
}

src/main/java/org/checkstyle/autofix/CheckstyleRecipeRegistry.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public final class CheckstyleRecipeRegistry {
4343
CheckConfiguration, Recipe>> RECIPE_MAP_WITH_CONFIG =
4444
new EnumMap<>(CheckstyleCheck.class);
4545

46+
private static final String HASH_SEPARATOR = "#";
47+
4648
static {
4749
RECIPE_MAP.put(CheckstyleCheck.UPPER_ELL, UpperEll::new);
4850
RECIPE_MAP.put(CheckstyleCheck.HEX_LITERAL_CASE, HexLiteralCase::new);
@@ -65,18 +67,49 @@ private CheckstyleRecipeRegistry() {
6567
* @return a list of generated Recipe objects
6668
*/
6769
public static List<Recipe> getRecipes(List<CheckstyleViolation> violations,
68-
Map<CheckstyleCheck, CheckConfiguration> config) {
70+
Map<CheckstyleConfigModule, CheckConfiguration> config) {
6971
return violations.stream()
7072
.collect(Collectors.groupingBy(CheckstyleViolation::getSource))
7173
.entrySet()
7274
.stream()
7375
.map(entry -> {
74-
return createRecipe(entry.getValue(), config.get(entry.getKey()));
76+
final CheckConfiguration configuration =
77+
findMatchingConfiguration(entry.getKey(), config);
78+
return createRecipe(entry.getValue(), configuration);
7579
})
7680
.filter(Objects::nonNull)
7781
.collect(Collectors.toList());
7882
}
7983

84+
private static CheckConfiguration findMatchingConfiguration(String source,
85+
Map<CheckstyleConfigModule, CheckConfiguration> config) {
86+
return config.entrySet().stream()
87+
.filter(configEntry -> matchesSource(configEntry.getKey(), source))
88+
.map(Map.Entry::getValue)
89+
.findFirst()
90+
.orElse(null);
91+
}
92+
93+
private static boolean matchesSource(CheckstyleConfigModule module, String source) {
94+
final boolean matches;
95+
if (source.contains(HASH_SEPARATOR)) {
96+
matches = matchesWithHashSeparator(module, source);
97+
}
98+
else {
99+
matches = module.matchesId(source) || module.matchesCheck(source);
100+
}
101+
return matches;
102+
}
103+
104+
private static boolean matchesWithHashSeparator(CheckstyleConfigModule module, String source) {
105+
final String[] parts = source.split(HASH_SEPARATOR, 2);
106+
final String checkPart = parts[0];
107+
final String idPart = parts[1];
108+
final boolean exactMatch = module.matchesCheck(checkPart) && module.matchesId(idPart);
109+
final boolean individualMatch = module.matchesId(source) || module.matchesCheck(source);
110+
return exactMatch || individualMatch;
111+
}
112+
80113
private static Recipe createRecipe(List<CheckstyleViolation> violations,
81114
CheckConfiguration checkConfig) {
82115
Recipe result = null;

src/main/java/org/checkstyle/autofix/parser/CheckstyleViolation.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
import java.nio.file.Path;
2121

22-
import org.checkstyle.autofix.CheckstyleCheck;
23-
2422
public final class CheckstyleViolation {
2523

2624
private final int line;
@@ -29,14 +27,14 @@ public final class CheckstyleViolation {
2927

3028
private final String severity;
3129

32-
private final CheckstyleCheck source;
30+
private final String source;
3331

3432
private final String message;
3533

3634
private final Path filePath;
3735

3836
public CheckstyleViolation(int line, int column, String severity,
39-
CheckstyleCheck source, String message, Path filePath) {
37+
String source, String message, Path filePath) {
4038
this.line = line;
4139
this.column = column;
4240
this.severity = severity;
@@ -46,7 +44,7 @@ public CheckstyleViolation(int line, int column, String severity,
4644
}
4745

4846
public CheckstyleViolation(int line, String severity,
49-
CheckstyleCheck source, String message, Path filePath) {
47+
String source, String message, Path filePath) {
5048
this(line, -1, severity, source, message, filePath);
5149
}
5250

@@ -58,7 +56,7 @@ public Integer getColumn() {
5856
return column;
5957
}
6058

61-
public CheckstyleCheck getSource() {
59+
public String getSource() {
6260
return source;
6361
}
6462

src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Properties;
2626

2727
import org.checkstyle.autofix.CheckstyleCheck;
28+
import org.checkstyle.autofix.CheckstyleConfigModule;
2829

2930
import com.puppycrawl.tools.checkstyle.PropertiesExpander;
3031
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
@@ -36,14 +37,19 @@ private ConfigurationLoader() {
3637
// utility class
3738
}
3839

39-
public static Map<CheckstyleCheck, CheckConfiguration> mapConfiguration(Configuration config) {
40-
final Map<CheckstyleCheck, CheckConfiguration> result = new HashMap<>();
40+
public static Map<CheckstyleConfigModule,
41+
CheckConfiguration> mapConfiguration(Configuration config) {
42+
43+
final Map<CheckstyleConfigModule, CheckConfiguration> result = new HashMap<>();
4144
final Map<String, String> inherited = getProperties(config);
4245

4346
final Optional<CheckstyleCheck> module = CheckstyleCheck.fromSource(config.getName());
4447
module.ifPresent(checkstyleCheck -> {
45-
result.put(checkstyleCheck, new CheckConfiguration(checkstyleCheck, new HashMap<>(),
46-
getProperties(config)));
48+
final Map<String, String> properties = getProperties(config);
49+
final CheckstyleConfigModule configModule = new CheckstyleConfigModule(
50+
checkstyleCheck, properties.get("id"));
51+
result.put(configModule,
52+
new CheckConfiguration(checkstyleCheck, new HashMap<>(), properties));
4753
});
4854

4955
for (Configuration child : config.getChildren()) {
@@ -69,7 +75,7 @@ private static Map<String, String> getProperties(Configuration config) {
6975
return props;
7076
}
7177

72-
public static Map<CheckstyleCheck, CheckConfiguration> loadConfiguration(
78+
public static Map<CheckstyleConfigModule, CheckConfiguration> loadConfiguration(
7379
String checkstyleConfigurationPath, String propFile) {
7480
Properties props = new Properties();
7581
if (propFile == null) {

src/main/java/org/checkstyle/autofix/parser/SarifReportParser.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
import java.util.List;
2626
import java.util.Optional;
2727

28-
import org.checkstyle.autofix.CheckstyleCheck;
29-
3028
import de.jcup.sarif_2_1_0.SarifSchema210ImportExportSupport;
3129
import de.jcup.sarif_2_1_0.model.PhysicalLocation;
3230
import de.jcup.sarif_2_1_0.model.Region;
@@ -57,17 +55,14 @@ public List<CheckstyleViolation> parse(Path reportPath) {
5755
for (final Run run: report.getRuns()) {
5856
if (run.getResults() != null) {
5957
run.getResults().forEach(resultEntry -> {
60-
CheckstyleCheck.fromSource(resultEntry.getRuleId()).ifPresent(check -> {
61-
final CheckstyleViolation violation = createViolation(check, resultEntry);
62-
result.add(violation);
63-
});
58+
result.add(createViolation(resultEntry.getRuleId(), resultEntry));
6459
});
6560
}
6661
}
6762
return result;
6863
}
6964

70-
private CheckstyleViolation createViolation(CheckstyleCheck check, Result result) {
65+
private CheckstyleViolation createViolation(String check, Result result) {
7166
final String severity = result.getLevel().name();
7267
final String message = result.getMessage().getText();
7368
final PhysicalLocation location = result.getLocations().get(0).getPhysicalLocation();

src/main/java/org/checkstyle/autofix/parser/XmlReportParser.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.util.Iterator;
2626
import java.util.List;
2727
import java.util.Objects;
28-
import java.util.Optional;
2928

3029
import javax.xml.stream.XMLEventReader;
3130
import javax.xml.stream.XMLInputFactory;
@@ -34,8 +33,6 @@
3433
import javax.xml.stream.events.StartElement;
3534
import javax.xml.stream.events.XMLEvent;
3635

37-
import org.checkstyle.autofix.CheckstyleCheck;
38-
3936
public class XmlReportParser implements ReportParser {
4037

4138
private static final String FILE_TAG = "file";
@@ -78,7 +75,7 @@ public List<CheckstyleViolation> parse(Path xmlPath) {
7875
}
7976
else if (ERROR_TAG.equals(startElementName)) {
8077
Objects.requireNonNull(filename, "File name can not be null");
81-
parseErrorTag(startElement, filename).ifPresent(result::add);
78+
result.add(parseErrorTag(startElement, filename));
8279
}
8380
}
8481
}
@@ -111,14 +108,13 @@ private String parseFileTag(StartElement startElement) {
111108
return fileName;
112109
}
113110

114-
private Optional<CheckstyleViolation> parseErrorTag(StartElement startElement,
111+
private CheckstyleViolation parseErrorTag(StartElement startElement,
115112
String filename) {
116113
int line = -1;
117114
int column = -1;
118115
String message = null;
119116
String severity = null;
120-
CheckstyleViolation violation = null;
121-
Optional<CheckstyleCheck> source = Optional.empty();
117+
String source = null;
122118

123119
final Iterator<Attribute> attributes = startElement.getAttributes();
124120
while (attributes.hasNext()) {
@@ -138,17 +134,14 @@ private Optional<CheckstyleViolation> parseErrorTag(StartElement startElement,
138134
message = attribute.getValue();
139135
break;
140136
case SOURCE_ATTR:
141-
source = CheckstyleCheck.fromSource(attribute.getValue());
137+
source = attribute.getValue();
142138
break;
143139
default:
144140
break;
145141
}
146142
}
147-
if (source.isPresent()) {
148-
violation = new CheckstyleViolation(line, column, severity,
149-
source.get(), message, Path.of(filename));
150-
}
151-
return Optional.ofNullable(violation);
143+
return new CheckstyleViolation(line, column, severity,
144+
source, message, Path.of(filename));
152145

153146
}
154147
}

src/test/java/org/checkstyle/autofix/parser/CheckstyleReportsParserTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.util.Map;
2626
import java.util.stream.Collectors;
2727

28-
import org.checkstyle.autofix.CheckstyleCheck;
2928
import org.junit.jupiter.api.Test;
3029

3130
public class CheckstyleReportsParserTest {
@@ -49,7 +48,7 @@ public void testParseFromResource() throws Exception {
4948
assertEquals(13, record.getColumn());
5049
assertEquals("error", record.getSeverity());
5150
assertEquals("Example message", record.getMessage());
52-
assertEquals(CheckstyleCheck.UPPER_ELL, record.getSource());
51+
assertEquals("com.puppycrawl.tools.checkstyle.checks.UpperEllCheck", record.getSource());
5352
assertEquals(Path.of("Example.java"), record.getFilePath());
5453
}
5554

src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/DiffSingleLocalTest.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
--- src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/InputSingleLocalTest.java
22
+++ src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/OutputSingleLocalTest.java
3-
@@ -15,37 +15,37 @@
3+
@@ -17,37 +17,37 @@
44
import java.util.HashMap;
55
import java.util.List;
66

0 commit comments

Comments
 (0)