diff --git a/config/checkstyle.properties b/config/checkstyle.properties index 48a0a95..97243d5 100644 --- a/config/checkstyle.properties +++ b/config/checkstyle.properties @@ -5,3 +5,4 @@ checkstyle.regexp.header.file=config/regexp-header.txt checkstyle.importcontrol.file=config/import-control.xml checkstyle.importcontroltest.file=config/import-control-test.xml checkstyle.java.version=17 +checkstyle.cache.file=.cache/checkstyle-cachefile diff --git a/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java b/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java index a4fd3a7..fb3b1b6 100644 --- a/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java +++ b/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java @@ -74,8 +74,9 @@ public String getPropertiesPath() { public List getRecipeList() { final List violations = CheckstyleReportParser .parse(Path.of(getViolationReportPath())); + final CheckConfiguration configuration = loadCheckstyleConfiguration(); - return CheckstyleRecipeRegistry.getRecipes(violations); + return CheckstyleRecipeRegistry.getRecipes(violations, configuration); } private CheckConfiguration loadCheckstyleConfiguration() { diff --git a/src/main/java/org/checkstyle/autofix/CheckstyleCheck.java b/src/main/java/org/checkstyle/autofix/CheckstyleCheck.java new file mode 100644 index 0000000..d23acf5 --- /dev/null +++ b/src/main/java/org/checkstyle/autofix/CheckstyleCheck.java @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. +// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.checkstyle.autofix; + +import java.util.Arrays; +import java.util.Optional; + +public enum CheckstyleCheck { + UPPERELL("com.puppycrawl.tools.checkstyle.checks.UpperEllCheck"), + HEADER("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"); + + private final String id; + + CheckstyleCheck(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public static Optional fromSource(String source) { + return Arrays.stream(values()) + .filter(check -> check.getId().equals(source)) + .findFirst(); + + } + +} diff --git a/src/main/java/org/checkstyle/autofix/CheckstyleRecipeRegistry.java b/src/main/java/org/checkstyle/autofix/CheckstyleRecipeRegistry.java index 6ea1ff4..5359be8 100644 --- a/src/main/java/org/checkstyle/autofix/CheckstyleRecipeRegistry.java +++ b/src/main/java/org/checkstyle/autofix/CheckstyleRecipeRegistry.java @@ -17,24 +17,33 @@ package org.checkstyle.autofix; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; +import org.checkstyle.autofix.parser.CheckConfiguration; import org.checkstyle.autofix.parser.CheckstyleViolation; +import org.checkstyle.autofix.recipe.Header; import org.checkstyle.autofix.recipe.UpperEll; import org.openrewrite.Recipe; public final class CheckstyleRecipeRegistry { - private static final Map, Recipe>> RECIPE_MAP = - new HashMap<>(); + private static final EnumMap, + Recipe>> RECIPE_MAP = new EnumMap<>(CheckstyleCheck.class); + + private static final EnumMap, + CheckConfiguration, Recipe>> RECIPE_MAP_WITH_CONFIG = + new EnumMap<>(CheckstyleCheck.class); static { - RECIPE_MAP.put("UpperEllCheck", UpperEll::new); + RECIPE_MAP.put(CheckstyleCheck.UPPERELL, UpperEll::new); + RECIPE_MAP_WITH_CONFIG.put(CheckstyleCheck.HEADER, Header::new); } private CheckstyleRecipeRegistry() { @@ -47,28 +56,51 @@ private CheckstyleRecipeRegistry() { * using the simple name of the check, and applies the factory to generate Recipe instances. * * @param violations the list of Checkstyle violations + * @param config the checkstyle configuration * @return a list of generated Recipe objects */ - public static List getRecipes(List violations) { + public static List getRecipes(List violations, + CheckConfiguration config) { + return violations.stream() + .collect(Collectors.groupingBy(CheckstyleViolation::getSource)) + .entrySet() + .stream() + .map(entry -> createRecipe(entry, config)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static Recipe createRecipe(Map.Entry> entry, + CheckConfiguration config) { + + Recipe recipe = null; - final Map> violationsByCheck = violations.stream() - .collect(Collectors.groupingBy(CheckstyleViolation::getSource)); + final Optional check = CheckstyleCheck.fromSource(entry.getKey()); - final List recipes = new ArrayList<>(); + if (check.isPresent()) { - for (Map.Entry> entry : violationsByCheck.entrySet()) { - final String checkName = entry.getKey(); - final String simpleCheckName = checkName - .substring(checkName.lastIndexOf('.') + 1); - final List checkViolations = entry.getValue(); + final CheckstyleCheck checkstyleCheck = check.get(); + final List violations = entry.getValue(); - final Function, Recipe> recipeFactory = - RECIPE_MAP.get(simpleCheckName); - if (recipeFactory != null) { - recipes.add(recipeFactory.apply(checkViolations)); + final BiFunction, CheckConfiguration, + Recipe> configRecipeFactory = RECIPE_MAP_WITH_CONFIG.get(checkstyleCheck); + + if (configRecipeFactory == null) { + final Function, Recipe> simpleRecipeFactory = + RECIPE_MAP.get(checkstyleCheck); + recipe = simpleRecipeFactory.apply(violations); + } + else { + final CheckConfiguration subConfig = + extractCheckConfiguration(config, checkstyleCheck.name()); + recipe = configRecipeFactory.apply(violations, subConfig); } } + return recipe; + } - return recipes; + private static CheckConfiguration extractCheckConfiguration(CheckConfiguration config, + String checkName) { + return config.getConfig(checkName); } } diff --git a/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java b/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java index 48d4d3e..295e85d 100644 --- a/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java +++ b/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java @@ -45,6 +45,10 @@ private CheckConfiguration getParent() { return parent; } + private List getChildren() { + return children; + } + public String getProperty(String key) { String value = null; @@ -94,12 +98,19 @@ public int[] getIntArray(String propertyName) { return result; } - public CheckConfiguration getChildConfig(String childName) { + public CheckConfiguration getConfig(String childName) { CheckConfiguration result = null; - for (CheckConfiguration child : children) { - if (childName.equals(child.getName())) { - result = child; - break; + if (name.equals(childName)) { + result = this; + } + else { + final List childrenList = getChildren(); + for (final CheckConfiguration current : childrenList) { + if (childName.equals(current.getName())) { + result = current; + break; + } + childrenList.addAll(current.getChildren()); } } return result; diff --git a/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java b/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java index 7ee2073..f3f4976 100644 --- a/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java +++ b/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -56,7 +57,8 @@ public static CheckConfiguration mapConfiguration(Configuration config) { simpleChildren[index] = mapConfiguration(checkstyleChildren[index]); } - return new CheckConfiguration(config.getName(), properties, List.of(simpleChildren)); + return new CheckConfiguration(config.getName().toUpperCase(Locale.ROOT), + properties, List.of(simpleChildren)); } public static CheckConfiguration loadConfiguration(String checkstyleConfigurationPath, diff --git a/src/test/java/org/checkstyle/autofix/CheckstyleRecipeRegistryTest.java b/src/test/java/org/checkstyle/autofix/CheckstyleRecipeRegistryTest.java deleted file mode 100644 index ba00170..0000000 --- a/src/test/java/org/checkstyle/autofix/CheckstyleRecipeRegistryTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////// -// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. -// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/////////////////////////////////////////////////////////////////////////////////////////////// - -package org.checkstyle.autofix; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.List; - -import org.checkstyle.autofix.parser.CheckstyleViolation; -import org.junit.jupiter.api.Test; -import org.openrewrite.Recipe; - -public class CheckstyleRecipeRegistryTest { - - @Test - void testGetRecipesReturnsCorrectRecipe() { - - final List violations = List.of( - new CheckstyleViolation(5, 10, "error", - "com.puppycrawl.tools.checkstyle.checks.UpperEllCheck", - "Use uppercase 'L' for long literals.", "Example1.java"), - - new CheckstyleViolation(15, 20, "error", - "com.puppycrawl.tools.checkstyle.checks.UpperEllCheck", - "Use uppercase 'L' for long literals.", "Example2.java"), - - new CheckstyleViolation(8, 12, "error", - "com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck", - "Variable should be declared final.", "Example3.java") - ); - - final List recipes = CheckstyleRecipeRegistry.getRecipes(violations); - - assertEquals(1, recipes.size(), "Should return one recipe"); - assertEquals("UpperEll recipe", recipes.get(0).getDisplayName()); - } - -}