-
Notifications
You must be signed in to change notification settings - Fork 87
#1031: integrate openrewrite #2299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
samuelkos17
wants to merge
29
commits into
devonfw:main
Choose a base branch
from
samuelkos17:feature/1031_integrate_with_openrewrite
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
deb7f46
add: add help information for the rewrite command
yurii-yu 0b18f92
add: add help information for the rewrite command
yurii-yu 2eecccc
add: add help information for the rewrite command
yurii-yu d746c6a
add: add help information for the rewrite command
yurii-yu a7a0312
update: add necessary commandlet and property
yurii-yu 6e5baf1
Merge remote-tracking branch 'refs/remotes/origin/main' into feature/…
yurii-yu 1568f82
Merge branch 'main' into feature/1031_intergrate_with_openrewrite
yurii-yu 1963978
Merge branch 'main' into feature/1031_intergrate_with_openrewrite
jan-vcapgemini 276d69f
Merge branch 'main' into feature/1031_intergrate_with_openrewrite
yurii-yu 9212855
Merge remote-tracking branch 'origin/feature/1031_intergrate_with_ope…
yurii-yu 6ea329a
Merge branch 'main' into feature/1031_intergrate_with_openrewrite
hohwille 6970443
add: help message for RefactorCommandlet
yurii-yu 10f82d7
add: wrapper of the configurable recipe
yurii-yu 5e29a3a
update: two configuration files (more recipes can be added in this way)
yurii-yu 3f843e3
update: keep names consistent with the names in the config file
yurii-yu faa40be
add: placeholder enum
yurii-yu 35eb98c
update: finish the implementation
yurii-yu 7bffb92
update: finish the feature
yurii-yu 631037c
Merge branch 'main' into feature/1031_intergrate_with_openrewrite
yurii-yu c32d2a1
fix: fix failed errors by correcting the text
yurii-yu 208291f
Merge remote-tracking branch 'upstream' into feature/1031_integrate_w…
samuelkos17 ad90c44
#1031: Updated the OpenRewrite implementation to work with current ve…
samuelkos17 3be68a7
Merge remote-tracking branch 'upstream' into feature/1031_integrate_w…
samuelkos17 cdd87e4
#1031: Refactored the Openrewrite implementation to fit standards and…
samuelkos17 6444a39
Merge remote-tracking branch 'origin' into feature/1031_integrate_wit…
samuelkos17 c58b4b1
#1031: applied spotless
samuelkos17 d67ab84
Merge branch 'main' into feature/1031_integrate_with_openrewrite
maybeec c849851
#1031: Code clean up
samuelkos17 46b3da6
Merge branch 'main' into feature/1031_integrate_with_openrewrite
samuelkos17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
164 changes: 164 additions & 0 deletions
164
cli/src/main/java/com/devonfw/tools/ide/commandlet/RewriteCommandlet.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| package com.devonfw.tools.ide.commandlet; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import com.devonfw.tools.ide.cli.CliException; | ||
| import com.devonfw.tools.ide.context.IdeContext; | ||
| import com.devonfw.tools.ide.property.EnumProperty; | ||
| import com.devonfw.tools.ide.tool.mvn.Mvn; | ||
| import com.devonfw.tools.ide.tool.openrewrite.RecipeManager; | ||
| import com.devonfw.tools.ide.tool.openrewrite.RecipeWrapper; | ||
| import com.devonfw.tools.ide.tool.openrewrite.RewriteRecipeEnum; | ||
|
|
||
| /** | ||
| * {@link Commandlet} for <a href="https://docs.openrewrite.org/">OpenRewrite</a> refactoring. | ||
| */ | ||
| public class RewriteCommandlet extends Commandlet { | ||
|
|
||
| private static final Logger LOG = LoggerFactory.getLogger(RewriteCommandlet.class); | ||
|
|
||
| public final EnumProperty<RewriteRecipeEnum> command; | ||
| private final RecipeManager recipeManager; | ||
|
|
||
| /** | ||
| * The constructor. | ||
| * | ||
| * @param context the {@link IdeContext}. | ||
| */ | ||
| public RewriteCommandlet(IdeContext context) { | ||
|
|
||
| super(context); | ||
| addKeyword(getName()); | ||
| this.command = add(new EnumProperty<>("", true, "recipe_name", RewriteRecipeEnum.class)); | ||
| this.recipeManager = new RecipeManager(); | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
|
|
||
| return "rewrite"; | ||
| } | ||
|
|
||
| /** | ||
| * Parses the raw MVN command from JSON into individual argument tokens, stripping the leading "mvn" if present. | ||
| * | ||
| * @param recipeRawCommands the raw command string from the recipe configuration. | ||
| * @return list of individual MVN argument tokens. | ||
| */ | ||
| private List<String> adaptMVNCommand(String recipeRawCommands) { | ||
| String trimmed = recipeRawCommands.trim(); | ||
| if (trimmed.startsWith("mvn ")) { | ||
| trimmed = trimmed.substring(4); | ||
| } else if (trimmed.equals("mvn")) { | ||
| trimmed = ""; | ||
| } | ||
| List<String> args = new ArrayList<>(); | ||
| for (String token : trimmed.split("\\s+")) { | ||
| if (!token.isEmpty()) { | ||
| args.add(token); | ||
| } | ||
| } | ||
| return args; | ||
| } | ||
|
|
||
| /** | ||
| * Converts a raw command to dry-run mode by replacing the ":run" goal with ":dryRun". | ||
| * | ||
| * @param recipeRawCommands the raw command string. | ||
| * @return the command with dry-run mode applied. | ||
| * @throws CliException if the command does not contain a ":run" goal to replace. | ||
| */ | ||
| private String changeToDryRunCommand(String recipeRawCommands) { | ||
| String result = recipeRawCommands.replace(":run", ":dryRun"); | ||
| if (result.equals(recipeRawCommands)) { | ||
| throw new CliException("Cannot convert to dry-run: command does not contain ':run' goal: " + recipeRawCommands); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| private void showInfo(RecipeWrapper wrapper) { | ||
| LOG.info("Recipe [{}], {}", wrapper.ideasyCommand.name(), wrapper.description); | ||
| LOG.info("Reference {}", wrapper.url); | ||
| LOG.info("Raw command: {}", wrapper.rawCmd); | ||
| } | ||
|
|
||
| private boolean confirmApplyChange() { | ||
|
|
||
| String input = this.context.askForInput( | ||
| "***Before making actual changes to the code, please confirm it seriously." | ||
| + " It is strongly recommended to perform a DRY-RUN first***\n" + | ||
| "Type yes to apply changes, or press other keys to perform DRY-RUN: "); | ||
|
|
||
| return input.equalsIgnoreCase("yes"); | ||
|
|
||
| } | ||
|
|
||
| /** | ||
| * Searches up from the current working directory to find the nearest pom.xml. | ||
| * | ||
| * @return the path to the project root containing pom.xml. | ||
| * @throws CliException if no pom.xml is found or cwd is not set. | ||
| */ | ||
| Path findProjectRoot() { | ||
| Path dir = this.context.getCwd(); | ||
| if (dir == null) { | ||
| throw new CliException("Cannot determine current working directory"); | ||
| } | ||
| while (dir != null) { | ||
| if (Files.exists(dir.resolve("pom.xml"))) { | ||
| return dir; | ||
| } | ||
| dir = dir.getParent(); | ||
| } | ||
| throw new CliException("No pom.xml found in current directory or any parent directory"); | ||
| } | ||
|
|
||
| @Override | ||
| public void doRun() { | ||
|
|
||
| LOG.info("{} called", getClass().getSimpleName()); | ||
|
|
||
| RewriteRecipeEnum recipeEnum = this.command.getValue(); | ||
|
|
||
| if (!recipeManager.isValidRecipeEnum(recipeEnum)) { | ||
| throw new CliException("Invalid recipe name: " + recipeEnum); | ||
| } | ||
|
|
||
| RecipeWrapper wrapper = recipeManager.getRecipeWrapper(recipeEnum); | ||
|
|
||
| Path projectRoot = findProjectRoot(); | ||
| LOG.info("Target project: {}", projectRoot); | ||
|
|
||
| showInfo(wrapper); | ||
|
|
||
| String commandLine = wrapper.rawCmd; | ||
|
|
||
| if (!confirmApplyChange()) { | ||
| commandLine = changeToDryRunCommand(commandLine); | ||
| } | ||
|
|
||
| LOG.info("Actual command line: {}", commandLine); | ||
|
|
||
| try { | ||
| List<String> args = adaptMVNCommand(commandLine); | ||
| // Inject -f <project-root> so Maven runs in the correct project | ||
| args.add(0, "-f"); | ||
| args.add(1, projectRoot.toString()); | ||
| getCommandlet(Mvn.class).runTool(args); | ||
| } catch (Exception e) { | ||
| throw new CliException("OpenRewrite execution failed for recipe '" + wrapper.ideasyCommand.name() + "': " + e.getMessage(), e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isIdeHomeRequired() { | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
cli/src/main/java/com/devonfw/tools/ide/tool/openrewrite/RecipeManager.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| package com.devonfw.tools.ide.tool.openrewrite; | ||
|
|
||
| import java.io.BufferedReader; | ||
| import java.io.IOException; | ||
| import java.io.InputStreamReader; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
|
|
||
| import com.devonfw.tools.ide.cli.CliException; | ||
| import com.devonfw.tools.ide.json.JsonMapping; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
|
||
| /** | ||
| * Manages the loading and lookup of OpenRewrite {@link RecipeWrapper} configurations from JSON. | ||
| */ | ||
| public class RecipeManager { | ||
|
|
||
| private static final String OPEN_REWRITE_CONFIG_JSON_PATH = "refactor/openrewrite.json"; | ||
|
|
||
| private final Map<RewriteRecipeEnum, RecipeWrapper> recipes; | ||
|
|
||
| public RecipeManager() { | ||
| try (BufferedReader reader = new BufferedReader(new InputStreamReader( | ||
| Objects.requireNonNull(RecipeManager.class.getClassLoader().getResourceAsStream(OPEN_REWRITE_CONFIG_JSON_PATH)), StandardCharsets.UTF_8))) { | ||
|
samuelkos17 marked this conversation as resolved.
Outdated
|
||
| ObjectMapper objectMapper = JsonMapping.create(); | ||
| List<RecipeWrapper> wrapperList = objectMapper.readValue(reader, objectMapper.getTypeFactory().constructCollectionType(List.class, RecipeWrapper.class)); | ||
|
|
||
| java.util.LinkedHashMap<RewriteRecipeEnum, RecipeWrapper> map = new java.util.LinkedHashMap<>(); | ||
| for (RecipeWrapper one : wrapperList) { | ||
| map.put(one.ideasyCommand, one); | ||
| } | ||
| this.recipes = java.util.Collections.unmodifiableMap(map); | ||
| } catch (IOException e) { | ||
| throw new CliException("Failed to load " + OPEN_REWRITE_CONFIG_JSON_PATH, e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns the list of all loaded {@link RecipeWrapper} configurations. | ||
| * | ||
| * @return an unmodifiable list of available recipes. | ||
| */ | ||
| public List<RecipeWrapper> listAvailableRecipes() { | ||
|
|
||
| return List.copyOf(recipes.values()); | ||
| } | ||
|
|
||
| private Optional<RecipeWrapper> findRecipeByName(String rawName) { | ||
| return recipes.values().stream().filter(x -> x.originName.equals(rawName)).findFirst(); | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a recipe with the given original OpenRewrite name exists in the configuration. | ||
| * | ||
| * @param rawName the original recipe name (e.g. {@code "java.format_autoformat"}). | ||
| * @return {@code true} if a matching recipe was found. | ||
| */ | ||
| public boolean isValidRecipeNameRawName(String rawName) { | ||
| return findRecipeByName(rawName).isPresent(); | ||
| } | ||
|
samuelkos17 marked this conversation as resolved.
Outdated
|
||
|
|
||
| /** | ||
| * Checks if a recipe for the given {@link RewriteRecipeEnum} exists in the configuration. | ||
| * | ||
| * @param recipeEnum the enum constant identifying the recipe. | ||
| * @return {@code true} if a matching recipe was found. | ||
| */ | ||
| public boolean isValidRecipeEnum(RewriteRecipeEnum recipeEnum) { | ||
| return recipes.containsKey(recipeEnum); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the {@link RecipeWrapper} for the given {@link RewriteRecipeEnum}. | ||
| * | ||
| * @param recipeEnum the enum constant identifying the recipe. | ||
| * @return the recipe wrapper. | ||
| */ | ||
| public RecipeWrapper getRecipeWrapper(RewriteRecipeEnum recipeEnum) { | ||
| return recipes.get(recipeEnum); | ||
| } | ||
| } | ||
26 changes: 26 additions & 0 deletions
26
cli/src/main/java/com/devonfw/tools/ide/tool/openrewrite/RecipeWrapper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package com.devonfw.tools.ide.tool.openrewrite; | ||
|
|
||
| import com.devonfw.tools.ide.json.JsonObject; | ||
|
|
||
| public class RecipeWrapper implements JsonObject { | ||
|
|
||
| public String description; | ||
| public String originName; | ||
| public String url; | ||
| public RewriteRecipeEnum ideasyCommand; | ||
| public String rawCmd; | ||
|
|
||
| public RecipeWrapper(String description, String originName, String url, RewriteRecipeEnum ideasyCommand, String rawCmd) { | ||
| this.description = description; | ||
| this.originName = originName; | ||
| this.url = url; | ||
| this.ideasyCommand = ideasyCommand; | ||
| this.rawCmd = rawCmd; | ||
| } | ||
|
|
||
| //in case of future need | ||
| public String getName() { | ||
|
samuelkos17 marked this conversation as resolved.
Outdated
|
||
| return this.originName; | ||
| } | ||
|
|
||
| } | ||
73 changes: 73 additions & 0 deletions
73
cli/src/main/java/com/devonfw/tools/ide/tool/openrewrite/RecipeWrapperJsonDeserializer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| package com.devonfw.tools.ide.tool.openrewrite; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import com.devonfw.tools.ide.json.JsonBuilder; | ||
| import com.devonfw.tools.ide.json.JsonObjectDeserializer; | ||
| import com.fasterxml.jackson.core.JsonParser; | ||
| import com.fasterxml.jackson.databind.DeserializationContext; | ||
|
|
||
| /** | ||
| * {@link JsonObjectDeserializer} for {@link RecipeWrapper}. | ||
| */ | ||
| public class RecipeWrapperJsonDeserializer extends JsonObjectDeserializer<RecipeWrapper> { | ||
|
|
||
| private static final Logger LOG = LoggerFactory.getLogger(RecipeWrapperJsonDeserializer.class); | ||
|
|
||
| @Override | ||
| protected JsonBuilder<RecipeWrapper> createBuilder() { | ||
|
|
||
| return new RecipeWrapperBuilder(); | ||
| } | ||
|
|
||
| private class RecipeWrapperBuilder extends JsonBuilder<RecipeWrapper> { | ||
|
|
||
| private String description; | ||
| private String originName; | ||
| private String url; | ||
| private RewriteRecipeEnum ideasyCommand; | ||
| private String rawCmd; | ||
|
|
||
| @Override | ||
| public void setProperty(String property, JsonParser p, DeserializationContext ctxt) throws IOException { | ||
|
|
||
| switch (property) { | ||
| case "description" -> { | ||
| this.description = readValueAsString(p, property, this.description); | ||
| } | ||
| case "origin_name" -> { | ||
| this.originName = readValueAsString(p, property, this.originName); | ||
| } | ||
| case "url" -> { | ||
| this.url = readValueAsString(p, property, this.url); | ||
| } | ||
| case "ideasy_command" -> { | ||
| String value = readValueAsString(p, property, null); | ||
| if (value != null) { | ||
| try { | ||
| this.ideasyCommand = RewriteRecipeEnum.valueOf(value); | ||
| } catch (IllegalArgumentException e) { | ||
| this.ideasyCommand = RewriteRecipeEnum.UNRECOGNIZED_RECIPE; | ||
| } | ||
| } | ||
| } | ||
| case "raw_cmd" -> { | ||
| this.rawCmd = readValueAsString(p, property, this.rawCmd); | ||
| } | ||
| default -> { | ||
| LOG.warn("Unknown recipe property: {}", property); | ||
| super.setProperty(property, p, ctxt); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public RecipeWrapper build() { | ||
|
|
||
| return new RecipeWrapper(this.description, this.originName, this.url, this.ideasyCommand, this.rawCmd); | ||
| } | ||
| } | ||
| } |
5 changes: 5 additions & 0 deletions
5
cli/src/main/java/com/devonfw/tools/ide/tool/openrewrite/RewriteRecipeEnum.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package com.devonfw.tools.ide.tool.openrewrite; | ||
|
|
||
| public enum RewriteRecipeEnum { | ||
| FORMAT_JAVA_CODE, REMOVE_BLANK_LINES, UNRECOGNIZED_RECIPE | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.