diff --git a/config/import-control.xml b/config/import-control.xml
index 483abd1..adf5ddb 100644
--- a/config/import-control.xml
+++ b/config/import-control.xml
@@ -11,4 +11,5 @@
+
diff --git a/pom.xml b/pom.xml
index be97b7a..94d7ab6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,13 @@
+
+ com.puppycrawl.tools
+ checkstyle
+ ${checkstyle.version}
+ tests
+ test
+
org.openrewrite
rewrite-test
diff --git a/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java b/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java
index 733bbbe..a4fd3a7 100644
--- a/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java
+++ b/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java
@@ -20,8 +20,10 @@
import java.nio.file.Path;
import java.util.List;
+import org.checkstyle.autofix.parser.CheckConfiguration;
import org.checkstyle.autofix.parser.CheckstyleReportParser;
import org.checkstyle.autofix.parser.CheckstyleViolation;
+import org.checkstyle.autofix.parser.ConfigurationLoader;
import org.openrewrite.Option;
import org.openrewrite.Recipe;
@@ -35,6 +37,17 @@ public class CheckstyleAutoFix extends Recipe {
example = "target/checkstyle/checkstyle-report.xml")
private String violationReportPath;
+ @Option(displayName = "Checkstyle config path",
+ description = "Path to the file containing Checkstyle configuration.",
+ example = "config/checkstyle.xml")
+ private String configurationPath;
+
+ @Option(displayName = "Checkstyle properties file path",
+ description = "Path to the file containing the Checkstyle Properties.",
+ example = "config/checkstyle.properties",
+ required = false)
+ private String propertiesPath;
+
@Override
public String getDisplayName() {
return "Checkstyle autoFix";
@@ -49,12 +62,23 @@ public String getViolationReportPath() {
return violationReportPath;
}
+ public String getConfigurationPath() {
+ return configurationPath;
+ }
+
+ public String getPropertiesPath() {
+ return propertiesPath;
+ }
+
@Override
public List getRecipeList() {
-
final List violations = CheckstyleReportParser
.parse(Path.of(getViolationReportPath()));
return CheckstyleRecipeRegistry.getRecipes(violations);
}
+
+ private CheckConfiguration loadCheckstyleConfiguration() {
+ return ConfigurationLoader.loadConfiguration(getConfigurationPath(), getPropertiesPath());
+ }
}
diff --git a/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java b/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java
new file mode 100644
index 0000000..5fefb27
--- /dev/null
+++ b/src/main/java/org/checkstyle/autofix/parser/CheckConfiguration.java
@@ -0,0 +1,108 @@
+///////////////////////////////////////////////////////////////////////////////////////////////
+// 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.parser;
+
+import java.util.List;
+import java.util.Map;
+
+public final class CheckConfiguration {
+ private final String name;
+ private final Map properties;
+ private final List children;
+ private CheckConfiguration parent;
+
+ public CheckConfiguration(String name,
+ Map properties, List children) {
+ this.name = name;
+ this.properties = properties;
+ this.children = children;
+
+ for (CheckConfiguration child : children) {
+ child.setParent(this);
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Map getProperties() {
+ return properties;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public String getProperty(String key) {
+ return properties.get(key);
+ }
+
+ public int getInt(String propertyName) {
+ final String value = properties.get(propertyName);
+ final int result;
+ try {
+ result = Integer.parseInt(value);
+ }
+ catch (NumberFormatException exception) {
+ throw new IllegalArgumentException("Invalid integer value for property '"
+ + propertyName + "': " + value, exception);
+ }
+ return result;
+ }
+
+ public boolean getBoolean(String propertyName) {
+ return Boolean.parseBoolean(properties.get(propertyName));
+ }
+
+ public int[] getIntArray(String propertyName) {
+ final String value = properties.get(propertyName);
+ final int[] result;
+ final String[] parts = value.split(",");
+ result = new int[parts.length];
+ for (int index = 0; index < parts.length; index++) {
+ try {
+ result[index] = Integer.parseInt(parts[index].trim());
+ }
+ catch (NumberFormatException exception) {
+ throw new IllegalArgumentException("Property '" + propertyName
+ + "' has an invalid integer value: " + parts[index].trim(), exception);
+ }
+ }
+ return result;
+ }
+
+ public CheckConfiguration getChildByName(String childName) {
+ CheckConfiguration result = null;
+ for (CheckConfiguration child : children) {
+ if (childName.equals(child.getName())) {
+ result = child;
+ break;
+ }
+ }
+ return result;
+ }
+
+ public CheckConfiguration getParent() {
+ return parent;
+ }
+
+ private void setParent(CheckConfiguration parent) {
+ this.parent = parent;
+ }
+}
diff --git a/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java b/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java
new file mode 100644
index 0000000..58745b4
--- /dev/null
+++ b/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java
@@ -0,0 +1,91 @@
+///////////////////////////////////////////////////////////////////////////////////////////////
+// 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.parser;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.puppycrawl.tools.checkstyle.PropertiesExpander;
+import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
+import com.puppycrawl.tools.checkstyle.api.Configuration;
+
+public final class ConfigurationLoader {
+
+ private ConfigurationLoader() {
+ // utility class
+ }
+
+ public static CheckConfiguration mapConfiguration(Configuration config) {
+ final Map properties = new HashMap<>();
+ final String[] propertyNames = config.getPropertyNames();
+ for (String propertyName : propertyNames) {
+ try {
+ final String value = config.getProperty(propertyName);
+ properties.put(propertyName, value);
+
+ }
+ catch (CheckstyleException exception) {
+ throw new IllegalStateException("Error getting property " + propertyName,
+ exception);
+ }
+ }
+
+ final Configuration[] checkstyleChildren = config.getChildren();
+ final CheckConfiguration[] simpleChildren =
+ new CheckConfiguration[checkstyleChildren.length];
+ for (int index = 0; index < checkstyleChildren.length; index++) {
+ simpleChildren[index] = mapConfiguration(checkstyleChildren[index]);
+ }
+
+ return new CheckConfiguration(config.getName(), properties, List.of(simpleChildren));
+ }
+
+ public static CheckConfiguration loadConfiguration(String checkstyleConfigurationPath,
+ String propFile) {
+ Properties props = new Properties();
+
+ if (propFile == null) {
+ props = System.getProperties();
+ }
+ else {
+ try (FileInputStream input = new FileInputStream(propFile)) {
+ props.load(input);
+ }
+ catch (IOException exception) {
+ throw new IllegalStateException("Failed to read: " + propFile, exception);
+ }
+ }
+
+ final Configuration checkstyleConfig;
+ try {
+ checkstyleConfig = com.puppycrawl.tools.checkstyle.ConfigurationLoader
+ .loadConfiguration(checkstyleConfigurationPath, new PropertiesExpander(props));
+ }
+ catch (CheckstyleException exception) {
+ throw new IllegalStateException("Failed to load configuration:"
+ + checkstyleConfigurationPath, exception);
+ }
+
+ return mapConfiguration(checkstyleConfig);
+ }
+
+}
diff --git a/src/main/resources/META-INF/rewrite/recipes.yml b/src/main/resources/META-INF/rewrite/recipes.yml
index 76b2c72..ae1b7de 100644
--- a/src/main/resources/META-INF/rewrite/recipes.yml
+++ b/src/main/resources/META-INF/rewrite/recipes.yml
@@ -15,3 +15,4 @@ tags:
recipeList:
- org.checkstyle.autofix.CheckstyleAutoFix:
violationReportPath: "target/checkstyle/checkstyle-report.xml"
+ configurationPath: "https://raw.githubusercontent.com/checkstyle/checkstyle/checkstyle-${checkstyle.version}/config/checkstyle-checks.xml"
diff --git a/src/test/java/org/checkstyle/autofix/recipe/AbstractRecipeTest.java b/src/test/java/org/checkstyle/autofix/recipe/AbstractRecipeTest.java
index 27bc248..7976cce 100644
--- a/src/test/java/org/checkstyle/autofix/recipe/AbstractRecipeTest.java
+++ b/src/test/java/org/checkstyle/autofix/recipe/AbstractRecipeTest.java
@@ -1,63 +1,115 @@
-///////////////////////////////////////////////////////////////////////////////////////////////
-// 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.recipe;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.openrewrite.java.Assertions.java;
-import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
import org.checkstyle.autofix.InputClassRenamer;
+import org.checkstyle.autofix.parser.CheckConfiguration;
+import org.checkstyle.autofix.parser.CheckstyleReportParser;
+import org.checkstyle.autofix.parser.CheckstyleViolation;
+import org.checkstyle.autofix.parser.ConfigurationLoader;
import org.openrewrite.Recipe;
import org.openrewrite.test.RewriteTest;
-public abstract class AbstractRecipeTest implements RewriteTest {
+import com.puppycrawl.tools.checkstyle.AbstractXmlTestSupport;
+import com.puppycrawl.tools.checkstyle.Checker;
+import com.puppycrawl.tools.checkstyle.XMLLogger;
+import com.puppycrawl.tools.checkstyle.api.Configuration;
+import com.puppycrawl.tools.checkstyle.bdd.InlineConfigParser;
+import com.puppycrawl.tools.checkstyle.bdd.TestInputConfiguration;
- private static final String BASE_TEST_RESOURCES_PATH = "src/test/resources/org"
- + "/checkstyle/autofix/recipe/";
+/**
+ * Simple base class for recipe tests that extends Checkstyle's AbstractXmlTestSupport.
+ */
+public abstract class AbstractRecipeTest extends AbstractXmlTestSupport implements RewriteTest {
- private Recipe createPreprocessingRecipe() {
- return new InputClassRenamer();
- }
+ /**
+ * Returns the subpackage name for test resources.
+ */
+ protected abstract String getSubpackage();
- protected abstract Recipe getRecipe();
+ /**
+ * Creates the recipe with violations and configs.
+ */
+ protected abstract Recipe createRecipe(List violations,
+ CheckConfiguration checkConfigs);
+
+ @Override
+ protected String getPackageLocation() {
+ return "org/checkstyle/autofix/recipe/" + getSubpackage();
+ }
- protected void testRecipe(String recipePath, String testCaseName) throws IOException {
- final String testCaseDir = testCaseName.toLowerCase();
+ /**
+ * Main verification method.
+ */
+ protected void verify(String testCaseName) throws Exception {
final String inputFileName = "Input" + testCaseName + ".java";
final String outputFileName = "Output" + testCaseName + ".java";
+ final String inputPath = testCaseName.toLowerCase() + "/" + inputFileName;
+ final String outputPath = testCaseName.toLowerCase() + "/" + outputFileName;
- final String beforeCode = Files.readString(Paths.get(BASE_TEST_RESOURCES_PATH
- + recipePath + "/" + testCaseDir + "/" + inputFileName));
+ final List violations = runCheckstyleAndGetViolations(inputPath);
- final String afterCode = Files.readString(Paths.get(BASE_TEST_RESOURCES_PATH
- + recipePath + "/" + testCaseDir + "/" + outputFileName));
+ final CheckConfiguration checkConfigs = getAllCheckConfigurations(inputPath);
+
+ final String beforeCode = readFile(getPath(inputPath));
+ final String expectedAfterCode = readFile(getPath(outputPath));
+
+ final Recipe preprocessingRecipe = new InputClassRenamer();
+ final Recipe mainRecipe = createRecipe(violations, checkConfigs);
+
+ testRecipe(beforeCode, expectedAfterCode, preprocessingRecipe, mainRecipe);
+ }
- final Recipe preprocessing = createPreprocessingRecipe();
- final Recipe mainRecipe = getRecipe();
+ private List runCheckstyleAndGetViolations(String inputPath)
+ throws Exception {
+
+ final String configFilePath = getPath(inputPath);
+ final TestInputConfiguration testInputConfiguration =
+ InlineConfigParser.parse(configFilePath);
+ final Configuration parsedConfig = testInputConfiguration.createConfiguration();
+
+ final Checker checker = createChecker(parsedConfig);
+ final ByteArrayOutputStream xmlOutput = new ByteArrayOutputStream();
+ final XMLLogger logger = new XMLLogger(xmlOutput, XMLLogger.OutputStreamOptions.CLOSE);
+ checker.addListener(logger);
+
+ final List filesToCheck = Collections.singletonList(new File(configFilePath));
+ checker.process(filesToCheck);
+
+ final Path tempXmlPath = Files.createTempFile("checkstyle-report", ".xml");
+ try {
+ Files.write(tempXmlPath, xmlOutput.toByteArray());
+ return CheckstyleReportParser.parse(tempXmlPath);
+ } finally {
+ Files.deleteIfExists(tempXmlPath);
+ }
+ }
+
+ private CheckConfiguration getAllCheckConfigurations(String inputPath) throws Exception {
+ final String configFilePath = getPath(inputPath);
+ final TestInputConfiguration testInputConfiguration =
+ InlineConfigParser.parse(configFilePath);
+ return ConfigurationLoader.mapConfiguration(testInputConfiguration.createConfiguration());
+ }
+ /**
+ * Helper method to test recipes using OpenRewrite testing framework.
+ */
+ private void testRecipe(String beforeCode, String expectedAfterCode,
+ Recipe... recipes) {
assertDoesNotThrow(() -> {
rewriteRun(
- spec -> spec.recipes(preprocessing, mainRecipe),
- java(beforeCode, afterCode)
+ spec -> spec.recipes(recipes),
+ java(beforeCode, expectedAfterCode)
);
});
}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/checkstyle/autofix/recipe/UpperEllTest.java b/src/test/java/org/checkstyle/autofix/recipe/UpperEllTest.java
index ab6e228..5db3731 100644
--- a/src/test/java/org/checkstyle/autofix/recipe/UpperEllTest.java
+++ b/src/test/java/org/checkstyle/autofix/recipe/UpperEllTest.java
@@ -21,6 +21,7 @@
import java.nio.file.Path;
import java.util.List;
+import org.checkstyle.autofix.parser.CheckConfiguration;
import org.checkstyle.autofix.parser.CheckstyleReportParser;
import org.checkstyle.autofix.parser.CheckstyleViolation;
import org.junit.jupiter.api.Test;
@@ -29,27 +30,29 @@
public class UpperEllTest extends AbstractRecipeTest {
@Override
- protected Recipe getRecipe() {
- final String reportPath = "src/test/resources/org/checkstyle/autofix/recipe/upperell"
- + "/report.xml";
+ protected String getSubpackage() {
+ return "upperell";
+ }
+
+ @Override
+ protected Recipe createRecipe(List violations,
+ CheckConfiguration checkConfigs) {
- final List violations =
- CheckstyleReportParser.parse(Path.of(reportPath));
return new UpperEll(violations);
}
@Test
- void hexOctalLiteralTest() throws IOException {
- testRecipe("upperell", "HexOctalLiteral");
+ void hexOctalLiteral() throws Exception {
+ verify("HexOctalLiteral");
}
@Test
- void complexLongLiterals() throws IOException {
- testRecipe("upperell", "ComplexLongLiterals");
+ void complexLongLiterals() throws Exception {
+ verify("ComplexLongLiterals");
}
@Test
- void stringAndCommentTest() throws IOException {
- testRecipe("upperell", "StringAndComments");
+ void stringAndComments() throws Exception {
+ verify("StringAndComments");
}
}
diff --git a/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/InputHexOctalLiteral.java b/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/InputHexOctalLiteral.java
index fbaf30c..8056a76 100644
--- a/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/InputHexOctalLiteral.java
+++ b/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/InputHexOctalLiteral.java
@@ -1,3 +1,9 @@
+/*
+com.puppycrawl.tools.checkstyle.checks.UpperEllCheck
+
+*/
+
+
package org.checkstyle.autofix.recipe.upperell.hexoctalliteral;
public class InputHexOctalLiteral {
@@ -12,4 +18,4 @@ public void calculateValues() {
long octalResult = 01234l + 0xDEADBEEFl; //suppressed violation for 0xDEADBEFl
long binaryResult = 0b11110000l;
}
-}
+}
\ No newline at end of file
diff --git a/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/OutputHexOctalLiteral.java b/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/OutputHexOctalLiteral.java
index 840f57a..8910f64 100644
--- a/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/OutputHexOctalLiteral.java
+++ b/src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/OutputHexOctalLiteral.java
@@ -1,3 +1,9 @@
+/*
+UpperEll
+
+
+*/
+
package org.checkstyle.autofix.recipe.upperell.hexoctalliteral;
public class OutputHexOctalLiteral {