refactor writeMavenDependencies implementation

This commit is contained in:
vudayani
2024-09-28 22:05:15 +05:30
committed by Martin Lippert
parent f9949d6480
commit 25f8eef426
28 changed files with 218 additions and 1120 deletions

View File

@@ -214,12 +214,6 @@
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>3.8.1</version>
<exclusions>
<exclusion>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- <dependency>
<groupId>javax.xml.bind</groupId>

View File

@@ -50,7 +50,7 @@ import org.springframework.ide.vscode.boot.java.beans.NamedDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.QualifierDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.ResourceDefinitionProvider;
import org.springframework.ide.vscode.boot.java.conditionalonresource.ConditionalOnResourceDefinitionProvider;
import org.springframework.ide.vscode.boot.java.copilot.ResponseModifier;
import org.springframework.ide.vscode.boot.java.copilot.util.ResponseModifier;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.DataQueryParameterDefinitionProvider;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.JdtDataQuerySemanticTokensProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;

View File

@@ -32,7 +32,7 @@ import org.springframework.ide.vscode.boot.java.beans.ProfileReferencesProvider;
import org.springframework.ide.vscode.boot.java.beans.QualifierReferencesProvider;
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalsLiveHoverProvider;
import org.springframework.ide.vscode.boot.java.copilot.CopilotAgentCommandHandler;
import org.springframework.ide.vscode.boot.java.copilot.ResponseModifier;
import org.springframework.ide.vscode.boot.java.copilot.util.ResponseModifier;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeLensEngine;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentHighlightEngine;

View File

@@ -21,6 +21,7 @@ import org.openrewrite.internal.InMemoryLargeSourceSet;
import org.openrewrite.maven.MavenParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.copilot.util.SpringCliException;
import org.springframework.util.StringUtils;
public abstract class AbstractInjectMavenActionHandler {
@@ -76,8 +77,7 @@ public abstract class AbstractInjectMavenActionHandler {
sourceFileWriter.write(result.getAfter().printAllTrimmed());
}
}
}
catch (IOException ex) {
} catch (IOException ex) {
throw new SpringCliException("Error writing to " + pomPath.toAbsolutePath(), ex);
}
}
@@ -86,7 +86,7 @@ public abstract class AbstractInjectMavenActionHandler {
List<Path> paths = new ArrayList<>();
paths.add(getPomPath());
MavenParser mavenParser = MavenParser.builder().build();
List<SourceFile> parsedPomFiles = mavenParser.parse(paths, cwd, getExecutionContext()).toList();
List<SourceFile> parsedPomFiles = mavenParser.parse(paths, null, getExecutionContext()).toList();
return createRecipe().run(new InMemoryLargeSourceSet(parsedPomFiles), getExecutionContext());
}

View File

@@ -10,13 +10,12 @@ import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.copilot.util.ResponseModifier;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.gson.JsonElement;
import com.google.j2objc.annotations.Weak;
public class CopilotAgentCommandHandler {
@@ -26,12 +25,12 @@ public class CopilotAgentCommandHandler {
private static final String CMD_COPILOT_AGENT_LSPEDITS = "sts/copilot/agent/lspEdits";
private final SimpleLanguageServer server;
private final JavaProjectFinder projectFinder;
private final SimpleLanguageServer server;
private final JavaProjectFinder projectFinder;
private final ResponseModifier responseModifier;
public CopilotAgentCommandHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, ResponseModifier responseModifier) {
public CopilotAgentCommandHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder,
ResponseModifier responseModifier) {
this.server = server;
this.projectFinder = projectFinder;
this.responseModifier = responseModifier;
@@ -42,7 +41,7 @@ public class CopilotAgentCommandHandler {
server.onCommand(CMD_COPILOT_AGENT_ENHANCERESPONSE, (params) -> {
return enhanceResponseHandler(params);
});
log.info("Registered command handler: {}",CMD_COPILOT_AGENT_ENHANCERESPONSE);
log.info("Registered command handler: {}", CMD_COPILOT_AGENT_ENHANCERESPONSE);
server.onCommand(CMD_COPILOT_AGENT_LSPEDITS, params -> {
try {
@@ -58,27 +57,23 @@ public class CopilotAgentCommandHandler {
log.info("Command Handler: ");
String response = ((JsonElement) params.getArguments().get(0)).getAsString();
String modifiedResp = responseModifier.modify(response);
return CompletableFuture.completedFuture(modifiedResp);
return CompletableFuture.completedFuture(modifiedResp);
}
private CompletableFuture<WorkspaceEdit> createLspEdits(ExecuteCommandParams params) throws IOException {
log.info("Command Handler for lsp edits: ");
String docURI = ((JsonElement) params.getArguments().get(0)).getAsString();
String path = ((JsonElement) params.getArguments().get(0)).getAsString();
String content = ((JsonElement) params.getArguments().get(2)).getAsString();
TextDocument doc = server.getTextDocumentService().getLatestSnapshot("file:///Users/vudayani/Desktop/spring-petclinic/README-ai-spring-petclinic.md");
IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(docURI)).get();
System.out.println("Project path "+project.getLocationUri() + " "+project.getLocationUri().getPath().toString());
List<ProjectArtifact> projectArtifacts = computeProjectArtifacts(content);
ProjectArtifactEditGenerator editGenerator = new ProjectArtifactEditGenerator(server.getTextDocumentService(), projectArtifacts,
Paths.get(project.getLocationUri()), docURI);
// Paths.get(project.getLocationUri()), docURI);
ProjectArtifactEditGenerator editGenerator = new ProjectArtifactEditGenerator(server.getTextDocumentService(),
projectArtifacts, Paths.get(project.getLocationUri()), docURI);
WorkspaceEdit we = editGenerator.process().getResult();
System.out.println("Final: \n "+ we.toString());
return CompletableFuture.completedFuture(we);
return CompletableFuture.completedFuture(we);
}
List<ProjectArtifact> computeProjectArtifacts(String response) {
ProjectArtifactCreator projectArtifactCreator = new ProjectArtifactCreator();
List<ProjectArtifact> projectArtifacts = projectArtifactCreator.create(response);

View File

@@ -5,21 +5,25 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.openrewrite.Recipe;
import org.openrewrite.SourceFile;
import org.openrewrite.config.DeclarativeRecipe;
import org.openrewrite.maven.MavenParser;
import org.springframework.ide.vscode.boot.java.copilot.recipe.AddDependencyRecipeFactory;
import org.springframework.ide.vscode.boot.java.copilot.recipe.AddManagedDependencyRecipeFactory;
import org.springframework.ide.vscode.boot.java.copilot.recipe.AddPluginRecipeFactory;
import org.springframework.ide.vscode.boot.java.copilot.recipe.InjectTextMavenRepositoryRecipe;
import org.springframework.ide.vscode.boot.java.copilot.util.MavenBuildPluginReader;
import org.springframework.ide.vscode.boot.java.copilot.util.MavenDependencyReader;
import org.springframework.ide.vscode.boot.java.copilot.util.MavenRepositoryReader;
import org.openrewrite.maven.AddDependency;
import org.openrewrite.maven.AddManagedDependency;
import org.openrewrite.maven.AddPlugin;
import org.openrewrite.maven.AddRepository;
import org.openrewrite.xml.XmlParser;
import org.openrewrite.xml.search.FindTags;
import org.openrewrite.xml.tree.Xml;
import org.openrewrite.xml.tree.Xml.Document;
import org.openrewrite.xml.tree.Xml.Tag;
public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler {
private List<InjectMavenDependency> dependencies;
private List<MavenDependencyMetadata> dependencies;
private List<InjectMavenBuildPlugin> buildPlugins;
@@ -27,6 +31,15 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler {
private List<InjectMavenDependencyManagement> dependencyManagements;
public record MavenDependencyMetadata(String groupId, String artifactId, String version, String scope, String type,
String classifier) {};
public record MavenPluginMetadata(String groupId, String artifactId, String version, String configuration,
String dependencies, String executions, String filePattern) {};
public record MavenRepositoryMetadata(String id, String url, String repoName, boolean snapshotsEnabled,
boolean releasesEnabled) {};
public InjectMavenActionHandler(TemplateEngine templateEngine, Map<String, Object> model, Path cwd) {
super(templateEngine, model, cwd);
this.dependencies = new ArrayList<>();
@@ -39,7 +52,7 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler {
return buildPlugins.add(buildPlugin);
}
public boolean injectDependency(InjectMavenDependency dependency) {
public boolean injectDependency(MavenDependencyMetadata dependency) {
return dependencies.add(dependency);
}
@@ -54,40 +67,106 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler {
protected Recipe createRecipe() {
DeclarativeRecipe aggregateRecipe = new DeclarativeRecipe("spring.cli.ai.MavenUpdates",
"Add Pom changes from AI", "", Collections.emptySet(), null, null, false, Collections.emptyList());
MavenParser mavenParser = MavenParser.builder().build();
for (InjectMavenDependency d : dependencies) {
String text = getTextToUse(d.getText(), "Inject Maven Dependency");
MavenDependencyReader mavenDependencyReader = new MavenDependencyReader();
String[] mavenDependencies = mavenDependencyReader.parseMavenSection(text);
for (String md : mavenDependencies) {
aggregateRecipe.getRecipeList().add(new AddDependencyRecipeFactory().create(md));
}
for (MavenDependencyMetadata dep : dependencies) {
if (dep != null) {
AddDependency addDependency = new AddDependency(dep.groupId(), dep.artifactId(), dep.version(), null,
dep.scope(), null, null, dep.type(), dep.classifier(), null, null, null);
aggregateRecipe.getRecipeList().add(addDependency);
}
}
for (InjectMavenBuildPlugin p : buildPlugins) {
String text = getTextToUse(p.getText(), "Inject Maven Build Plugin");
MavenBuildPluginReader mavenBuildPluginReader = new MavenBuildPluginReader();
String[] buildPlugins = mavenBuildPluginReader.parseMavenSection(text);
for (String mp : buildPlugins) {
aggregateRecipe.getRecipeList().add(new AddPluginRecipeFactory().create(mp));
List<Xml.Document> xmlDocuments = parseToXml(p.getText());
for (Xml.Document xmlDocument : xmlDocuments) {
MavenPluginMetadata pm = findMavenPluginTags(xmlDocument);
if (pm != null) {
AddPlugin addPlugin = new AddPlugin(pm.groupId(), pm.artifactId(), pm.version(), pm.configuration(),
pm.dependencies(), pm.executions(), pm.filePattern());
aggregateRecipe.getRecipeList().add(addPlugin);
}
}
}
for (InjectMavenRepository r : repositories) {
String text = getTextToUse(r.getText(), "Inject Maven Repository");
MavenRepositoryReader mavenRepositoryReader = new MavenRepositoryReader();
String[] mavenRepositories = mavenRepositoryReader.parseMavenSection(text);
for (String mr : mavenRepositories) {
aggregateRecipe.getRecipeList().add(new InjectTextMavenRepositoryRecipe(mr));
List<Xml.Document> xmlDocuments = parseToXml(r.getText());
for (Xml.Document xmlDocument : xmlDocuments) {
MavenRepositoryMetadata rm = findRepositoryTags(xmlDocument);
if (rm != null) {
AddRepository addRepository = new AddRepository(rm.id(), rm.url(), rm.repoName(), null,
rm.snapshotsEnabled(), null, null, rm.releasesEnabled(), null, null, null);
aggregateRecipe.getRecipeList().add(addRepository);
}
}
}
for (InjectMavenDependencyManagement dm : dependencyManagements) {
String text = getTextToUse(dm.getText(), "Inject Maven Dependency Management");
MavenDependencyReader mavenDependencyReader = new MavenDependencyReader();
String[] mavenDependencyManagements = mavenDependencyReader.parseMavenSection(text);
for (String mdm : mavenDependencyManagements) {
aggregateRecipe.getRecipeList().add(new AddManagedDependencyRecipeFactory().create(mdm));
List<Xml.Document> xmlDocuments = parseToXml(dm.getText());
for (Xml.Document xmlDocument : xmlDocuments) {
MavenDependencyMetadata mdm = findMavenDependencyTags(xmlDocument);
if (mdm != null) {
AddManagedDependency addManagedDependency = new AddManagedDependency(mdm.groupId(), mdm.artifactId(),
mdm.version(), mdm.scope(), null, mdm.classifier(), null, null, null, null);
aggregateRecipe.getRecipeList().add(addManagedDependency);
}
}
}
return aggregateRecipe;
}
public List<Xml.Document> parseToXml(String content) {
XmlParser parser = new XmlParser();
List<SourceFile> sourceFiles = parser.parse(content).collect(Collectors.toList());
List<Xml.Document> xmlDocuments = sourceFiles.stream().filter(sourceFile -> sourceFile instanceof Xml.Document)
.map(sourceFile -> (Xml.Document) sourceFile).collect(Collectors.toList());
return xmlDocuments;
}
public MavenDependencyMetadata findMavenDependencyTags(Xml.Document xmlDocument) {
Set<Tag> dependencyTags = FindTags.find(xmlDocument, "//dependency");
for (Tag dependencyTag : dependencyTags) {
String groupId = dependencyTag.getChildValue("groupId").orElse(null);
String artifactId = dependencyTag.getChildValue("artifactId").orElse(null);
String version = dependencyTag.getChildValue("version").orElse("latest");
String scope = dependencyTag.getChildValue("scope").orElse(null);
String type = dependencyTag.getChildValue("type").orElse(null);
String classifier = dependencyTag.getChildValue("classifier").orElse(null);
if (groupId != null && artifactId != null && version != null)
return new MavenDependencyMetadata(groupId, artifactId, version, scope, type, classifier);
}
return null;
}
public MavenPluginMetadata findMavenPluginTags(Xml.Document xmlDocument) {
Set<Tag> pluginTags = FindTags.find(xmlDocument, "//plugin");
for (Tag pluginTag : pluginTags) {
String groupId = pluginTag.getChildValue("groupId").orElse(null);
String artifactId = pluginTag.getChildValue("artifactId").orElse(null);
String version = pluginTag.getChildValue("version").orElse("latest");
String configuration = pluginTag.getChildValue("configuration").orElse(null);
String dependencies = pluginTag.getChildValue("dependencies").orElse(null);
String executions = pluginTag.getChildValue("executions").orElse(null);
String filePattern = pluginTag.getChildValue("filePattern").orElse(null);
if (groupId != null && artifactId != null && version != null)
return new MavenPluginMetadata(groupId, artifactId, version, configuration, dependencies, executions,
filePattern);
}
return null;
}
private MavenRepositoryMetadata findRepositoryTags(Document xmlDocument) {
Set<Tag> repoTags = FindTags.find(xmlDocument, "//plugin");
for (Tag repoTag : repoTags) {
String id = repoTag.getChildValue("id").orElse(null);
String url = repoTag.getChildValue("url").orElse(null);
String repoName = repoTag.getChildValue("repoName").orElse("latest");
boolean snapshotsEnabled = Boolean.parseBoolean(repoTag.getChildValue("snapshotsEnabled").orElse(null));
boolean releasesEnabled = Boolean.parseBoolean(repoTag.getChildValue("releasesEnabled").orElse(null));
if (id != null && url != null)
return new MavenRepositoryMetadata(id, url, repoName, snapshotsEnabled, releasesEnabled);
}
return null;
}
}

View File

@@ -1,29 +0,0 @@
package org.springframework.ide.vscode.boot.java.copilot;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Mark Pollack
*/
public class InjectMavenDependency {
private String text;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public InjectMavenDependency(@JsonProperty("text") String text) {
this.text = Objects.requireNonNull(text);
}
public String getText() {
return text;
}
@Override
public String toString() {
return "InjectMavenDependency{" + "text='" + text + '\'' + '}';
}
}

View File

@@ -24,9 +24,12 @@ import org.apache.maven.model.Model;
import org.eclipse.lsp4j.ChangeAnnotation;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.openrewrite.Result;
import org.openrewrite.xml.tree.Xml;
import org.springframework.ide.vscode.boot.java.copilot.InjectMavenActionHandler.MavenDependencyMetadata;
import org.springframework.ide.vscode.boot.java.copilot.util.ClassNameExtractor;
import org.springframework.ide.vscode.boot.java.copilot.util.MavenDependencyReader;
import org.springframework.ide.vscode.boot.java.copilot.util.PomReader;
import org.springframework.ide.vscode.boot.java.copilot.util.PropertyFileUtils;
import org.springframework.ide.vscode.boot.java.copilot.util.SpringCliException;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.rewrite.ORDocUtils;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -42,10 +45,11 @@ public class ProjectArtifactEditGenerator {
private final Pattern compiledGroupIdPattern;
private final Pattern compiledArtifactIdPattern;
private final SimpleTextDocumentService simpleTextDocumentService;
public ProjectArtifactEditGenerator(SimpleTextDocumentService simpleTextDocumentService, List<ProjectArtifact> projectArtifacts, Path projectPath, String readmeFileName) {
public ProjectArtifactEditGenerator(SimpleTextDocumentService simpleTextDocumentService,
List<ProjectArtifact> projectArtifacts, Path projectPath, String readmeFileName) {
this.simpleTextDocumentService = simpleTextDocumentService;
this.projectArtifacts = projectArtifacts;
this.projectPath = projectPath;
@@ -68,48 +72,47 @@ public class ProjectArtifactEditGenerator {
we.setDocumentChanges(new ArrayList<>());
we.setChangeAnnotations(Map.of(changeAnnotationId, changeAnnotation));
for (ProjectArtifact projectArtifact : projectArtifacts) {
// try {
ProjectArtifactType artifactType = projectArtifact.getArtifactType();
switch (artifactType) {
case SOURCE_CODE:
writeSourceCode(projectArtifact, projectPath, changeAnnotationId, we);
break;
case TEST_CODE:
writeTestCode(projectArtifact, projectPath, changeAnnotationId, we);
break;
case MAVEN_DEPENDENCIES:
writeMavenDependencies(projectArtifact, projectPath,changeAnnotationId, we);
break;
case APPLICATION_PROPERTIES:
writeApplicationProperties(projectArtifact, projectPath, changeAnnotationId, we);
break;
case MAIN_CLASS:
updateMainApplicationClassAnnotations(projectArtifact, projectPath,
changeAnnotationId, we);
break;
case HTML:
writeHtml(projectArtifact, projectPath, changeAnnotationId, we);
break;
default:
processArtifactResult.addToNotProcessed(projectArtifact);
break;
case SOURCE_CODE:
writeSourceCode(projectArtifact, projectPath, changeAnnotationId, we);
break;
case TEST_CODE:
writeTestCode(projectArtifact, projectPath, changeAnnotationId, we);
break;
case MAVEN_DEPENDENCIES:
writeMavenDependencies(projectArtifact, projectPath, changeAnnotationId, we);
break;
case APPLICATION_PROPERTIES:
writeApplicationProperties(projectArtifact, projectPath, changeAnnotationId, we);
break;
case MAIN_CLASS:
updateMainApplicationClassAnnotations(projectArtifact, projectPath, changeAnnotationId, we);
break;
case HTML:
writeHtml(projectArtifact, projectPath, changeAnnotationId, we);
break;
default:
processArtifactResult.addToNotProcessed(projectArtifact);
break;
}
}
processArtifactResult.setResult(we);
return processArtifactResult;
}
private void writeSourceCode(ProjectArtifact projectArtifact, Path projectPath,
String changeAnnotationId, WorkspaceEdit we) throws IOException {
private void writeSourceCode(ProjectArtifact projectArtifact, Path projectPath, String changeAnnotationId,
WorkspaceEdit we) throws IOException {
String packageName = this.calculatePackageForArtifact(projectArtifact);
ClassNameExtractor classNameExtractor = new ClassNameExtractor();
Optional<String> className = classNameExtractor.extractClassName(projectArtifact.getText());
if (className.isPresent()) {
Path output = resolveSourceFile(projectPath, packageName, className.get() + ".java");
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, output.toUri().toASCIIString(), getFileContent(output), projectArtifact.getText(), changeAnnotationId, we);
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, output.toUri().toASCIIString(),
getFileContent(output), projectArtifact.getText(), changeAnnotationId, we);
}
}
private String getFileContent(Path file) throws IOException {
TextDocument doc = simpleTextDocumentService.getLatestSnapshot(file.toUri().toASCIIString());
if (doc != null) {
@@ -118,19 +121,21 @@ public class ProjectArtifactEditGenerator {
return null;
}
private void writeTestCode(ProjectArtifact projectArtifact, Path projectPath,
String changeAnnotationId, WorkspaceEdit we) throws IOException {
private void writeTestCode(ProjectArtifact projectArtifact, Path projectPath, String changeAnnotationId,
WorkspaceEdit we) throws IOException {
// TODO parameterize better to reduce code duplication
String packageName = this.calculatePackageForArtifact(projectArtifact);
ClassNameExtractor classNameExtractor = new ClassNameExtractor();
Optional<String> className = classNameExtractor.extractClassName(projectArtifact.getText());
if (className.isPresent()) {
Path output = resolveTestFile(projectPath, packageName, className.get() + ".java");
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, output.toUri().toASCIIString(), getFileContent(output), projectArtifact.getText(), changeAnnotationId, we);
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, output.toUri().toASCIIString(),
getFileContent(output), projectArtifact.getText(), changeAnnotationId, we);
}
}
private void writeMavenDependencies(ProjectArtifact projectArtifact, Path projectPath, String changeAnnotationId, WorkspaceEdit we) {
private void writeMavenDependencies(ProjectArtifact projectArtifact, Path projectPath, String changeAnnotationId,
WorkspaceEdit we) {
PomReader pomReader = new PomReader();
Path currentProjectPomPath = this.projectPath.resolve("pom.xml");
if (Files.notExists(currentProjectPomPath)) {
@@ -140,46 +145,29 @@ public class ProjectArtifactEditGenerator {
Model currentModel = pomReader.readPom(currentProjectPomPath.toFile());
List<Dependency> currentDependencies = currentModel.getDependencies();
MavenDependencyReader mavenDependencyReader = new MavenDependencyReader();
// projectArtifact.getText() contains a list of <dependency> elements
String[] mavenDependencies = mavenDependencyReader.parseMavenSection(projectArtifact.getText());
InjectMavenActionHandler injectMavenActionHandler = new InjectMavenActionHandler(null, new HashMap<>(),
projectPath);
for (String candidateDependencyText : mavenDependencies) {
if (!candidateDependencyAlreadyPresent(getProjectDependency(candidateDependencyText),
currentDependencies)) {
injectMavenActionHandler.injectDependency(new InjectMavenDependency(candidateDependencyText));
List<Xml.Document> xmlDocuments = injectMavenActionHandler.parseToXml(projectArtifact.getText());
for (Xml.Document xmlDocument : xmlDocuments) {
MavenDependencyMetadata dep = injectMavenActionHandler.findMavenDependencyTags(xmlDocument);
if (!candidateDependencyAlreadyPresent(dep, currentDependencies)) {
injectMavenActionHandler.injectDependency(dep);
}
}
List<Result> res = injectMavenActionHandler.run().getChangeset().getAllResults();
if(!res.isEmpty()) {
WorkspaceEdit workspaceEdit = ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, res, changeAnnotationId).get();
we.getDocumentChanges().addAll(workspaceEdit.getDocumentChanges());
if (!res.isEmpty()) {
WorkspaceEdit workspaceEdit = ORDocUtils
.createWorkspaceEdit(simpleTextDocumentService, res, changeAnnotationId).get();
we.getDocumentChanges().addAll(workspaceEdit.getDocumentChanges());
}
// return res.isEmpty() ? Collections.emptyList() :
// ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, res, changeAnnotationId).get();
// convertToEdits(res, changeAnnotationId);
}
// private List<Lsp.ChangeOperation> convertToEdits(List<Result> allResults, String changeAnnotationId) {
// List<Lsp.ChangeOperation> edits = new ArrayList<>();
// for (Result res : allResults) {
// Path p = (res.getBefore() == null) ? res.getAfter().getSourcePath() : res.getBefore().getSourcePath();
// String uri = p.toUri().toASCIIString();
// ConversionUtils
// .computeTextDocEdit(uri, res.getBefore().printAll(), res.getAfter().printAll(), changeAnnotationId)
// .ifPresent(edits::add);
// }
// return edits;
// }
private boolean candidateDependencyAlreadyPresent(ProjectDependency toMergeDependency,
private boolean candidateDependencyAlreadyPresent(MavenDependencyMetadata dep,
List<Dependency> currentDependencies) {
String candidateGroupId = toMergeDependency.getGroupId();
String candidateArtifactId = toMergeDependency.getArtifactId();
String candidateGroupId = dep.groupId();
String candidateArtifactId = dep.artifactId();
boolean candidateDependencyAlreadyPresent = false;
for (Dependency currentDependency : currentDependencies) {
String currentGroupId = currentDependency.getGroupId();
@@ -193,37 +181,10 @@ public class ProjectArtifactEditGenerator {
}
private ProjectDependency getProjectDependency(String xml) {
String groupId = null;
String artifactId = null;
try {
groupId = extractValue(xml, this.compiledGroupIdPattern);
artifactId = extractValue(xml, this.compiledArtifactIdPattern);
}
catch (Exception ex) {
throw new SpringCliException("Exception processing dependency: " + xml, ex);
}
if (groupId == null || artifactId == null) {
throw new SpringCliException("Could not process dependency: " + xml);
}
return new ProjectDependency(groupId, artifactId);
}
private static String extractValue(String xml, Pattern compiledPattern) {
Matcher matcher = compiledPattern.matcher(xml);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}
private void writeApplicationProperties(ProjectArtifact projectArtifact, Path projectPath,
String changeAnnotationId, WorkspaceEdit we) throws IOException {
Path applicationPropertiesPath = projectPath.resolve("src")
.resolve("main")
.resolve("resources")
.resolve("application.properties");
Path applicationPropertiesPath = projectPath.resolve("src").resolve("main").resolve("resources")
.resolve("application.properties");
Properties srcProperties = new Properties();
Properties destProperties = new Properties();
@@ -237,22 +198,24 @@ public class ProjectArtifactEditGenerator {
mergedProperties.store(sw, "updated by spring ai add");
sw.flush();
String newContent = sw.getBuffer().toString();
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, applicationPropertiesPath.toUri().toASCIIString(), getFileContent(applicationPropertiesPath), newContent, changeAnnotationId, we);
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, applicationPropertiesPath.toUri().toASCIIString(),
getFileContent(applicationPropertiesPath), newContent, changeAnnotationId, we);
}
private void updateMainApplicationClassAnnotations(ProjectArtifact projectArtifact,
Path projectPath, String changeAnnotationId, WorkspaceEdit we) {
private void updateMainApplicationClassAnnotations(ProjectArtifact projectArtifact, Path projectPath,
String changeAnnotationId, WorkspaceEdit we) {
// TODO mer
// return Collections.emptyList();
}
private void writeHtml(ProjectArtifact projectArtifact, Path projectPath,
String changeAnnotationId, WorkspaceEdit we) throws IOException {
private void writeHtml(ProjectArtifact projectArtifact, Path projectPath, String changeAnnotationId,
WorkspaceEdit we) throws IOException {
String html = projectArtifact.getText();
String fileName = extractFilenameFromComment(html);
if (fileName != null) {
Path htmlFile = projectPath.resolve(fileName);
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, fileName, getFileContent(htmlFile), projectArtifact.getText(), changeAnnotationId, we);
ORDocUtils.createWorkspaceEdit(simpleTextDocumentService, fileName, getFileContent(htmlFile),
projectArtifact.getText(), changeAnnotationId, we);
}
}
@@ -270,8 +233,7 @@ public class ProjectArtifactEditGenerator {
packageToUse = matcher.group(1);
}
}
}
catch (IOException ex) {
} catch (IOException ex) {
throw new SpringCliException(
"Could not parse package name from Project Artifact: " + projectArtifact.getText(), ex);
}

View File

@@ -1,27 +0,0 @@
package org.springframework.ide.vscode.boot.java.copilot;
public class ProjectDependency {
private final String groupId;
private final String artifactId;
public ProjectDependency(String groupId, String artifactId) {
this.groupId = groupId;
this.artifactId = artifactId;
}
public String getGroupId() {
return groupId;
}
public String getArtifactId() {
return artifactId;
}
@Override
public String toString() {
return "ProjectDependency{" + "groupId='" + groupId + '\'' + ", artifactId='" + artifactId + '\'' + '}';
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2021-2023 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
/**
* @author Fabian Krüger
*/
public class AbstractRecipeFactory {
private static XmlMapper mapper = new XmlMapper();
@org.jetbrains.annotations.Nullable
protected static String getNullOrTextValue(JsonNode jsonNode, String field) {
return (jsonNode.get(field) != null) ? AbstractRecipeFactory.getTextValue(jsonNode, field) : null;
}
protected static String getTextValue(JsonNode jsonNode, String field) {
try {
JsonNode xmlNode = jsonNode.get(field);
return xmlNode.textValue();
}
catch (NullPointerException npe) {
throw new RecipeCreationException(
"Could not get text value for field '%s' from: \n%s".formatted(field, jsonNode.toPrettyString()));
}
}
protected static JsonNode getJsonNode(String mavenDependencySnippet) throws JsonProcessingException {
JsonNode jsonNode = mapper.readTree(mavenDependencySnippet);
return jsonNode;
}
protected String getTextOrDefaultValue(JsonNode jsonNode, String version, String defaultValue) {
String nullOrTextValue = getNullOrTextValue(jsonNode, version);
return (nullOrTextValue != null) ? nullOrTextValue : defaultValue;
}
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2021-2023 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import java.util.regex.Pattern;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.maven.AddDependencyVisitor;
import org.openrewrite.maven.table.MavenMetadataFailures;
/**
* Alternative to {@link org.openrewrite.maven.AddDependency} not doing any version
* checks. It uses the {@link AddDependencyVisitor} and bypasses all checks and other code
* in {@link org.openrewrite.maven.AddManagedDependency}.
*
* @author Fabian Krüger
*/
public class AddDependencyRecipe extends Recipe {
private final String groupId;
private final String artifactId;
private final String version;
private final String scope;
private final String type;
private final @Nullable String classifier;
private final @Nullable Boolean optional;
private final Pattern familyRegex;
private final MavenMetadataFailures metadataFailures;
public AddDependencyRecipe(String groupId, String artifactId, String version, String scope, String type,
@Nullable String classifier, @Nullable Boolean optional, Pattern familyRegex,
MavenMetadataFailures metadataFailures) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.scope = scope;
this.type = type;
this.classifier = classifier;
this.optional = optional;
this.familyRegex = familyRegex;
this.metadataFailures = metadataFailures;
}
@Override
public String getDisplayName() {
return "Add dependency '%s:%s'".formatted(groupId, artifactId);
}
@Override
public String getDescription() {
return getDisplayName();
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
AddDependencyVisitor addDependencyVisitor = new AddDependencyVisitor(groupId, artifactId, version, null, scope,
true, type, classifier, optional, familyRegex, metadataFailures);
return addDependencyVisitor;
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2021-2024 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import java.util.regex.Pattern;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.maven.AddDependency;
import org.openrewrite.maven.table.MavenMetadataFailures;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
/**
* @author Fabian Krüger
*/
public class AddDependencyRecipeFactory extends AbstractRecipeFactory {
/**
* Create {@link AddDependency} recipe from Maven dependency XML snippet.
*
* <pre>
* {@code
* <dependency>
* <groupId>groupId</groupId>
* <artifactId>artifactId</artifactId>
* <version>${some.version}</version>
* <classifier>classifier</classifier>
* <scope>scope</scope>
* <type>pom</type>
* <optional>true</optional>
* </dependency>
* }
* </pre>
*
*/
public AddDependencyRecipe create(String mavenDependency) {
try {
JsonNode jsonNode = getJsonNode(mavenDependency);
String groupId = getTextValue(jsonNode, "groupId");
String artifactId = getTextValue(jsonNode, "artifactId");
String version = getTextOrDefaultValue(jsonNode, "version", "latest");
@Nullable
String scope = getNullOrTextValue(jsonNode, "scope");
@Nullable
String type = getNullOrTextValue(jsonNode, "type");
@Nullable
String classifier = getNullOrTextValue(jsonNode, "classifier");
@Nullable
Boolean optional = Boolean.parseBoolean(getNullOrTextValue(jsonNode, "optional"));
@Nullable
String familyPattern = null;
Pattern familyRegex = (familyPattern != null) ? Pattern.compile(familyPattern) : null;
MavenMetadataFailures metadataFailures = null;
AddDependencyRecipe recipe = new AddDependencyRecipe(groupId, artifactId, version, scope, type, classifier,
optional, familyRegex, metadataFailures);
return recipe;
}
catch (JsonProcessingException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2021-2023 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.maven.AddManagedDependencyVisitor;
/**
* Alternative to {@link org.openrewrite.maven.AddManagedDependency} that doesn't verify
* the provided version. It uses the {@link AddManagedDependencyVisitor} and bypasses all
* checks and other code in {@link org.openrewrite.maven.AddManagedDependency}.
*
* @author Fabian Krüger
*/
public class AddManagedDependencyRecipe extends Recipe {
private final String groupId;
private final String artifactId;
private final String version;
private final String scope;
private final String type;
private final String classifier;
public AddManagedDependencyRecipe(String groupId, String artifactId, String version, String scope, String type,
String classifier) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.scope = scope;
this.type = type;
this.classifier = classifier;
}
@Override
public String getDisplayName() {
return "Add managed dependency '%s:%s'".formatted(groupId, artifactId);
}
@Override
public String getDescription() {
return getDisplayName();
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new AddManagedDependencyVisitor(groupId, artifactId, version, scope, type, classifier);
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2021-2023 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.maven.model.Dependency;
import org.openrewrite.internal.lang.Nullable;
/**
* @author Fabian Krüger
*/
public class AddManagedDependencyRecipeFactory extends AbstractRecipeFactory {
/**
* Create a {@link AddManagedDependencyRecipe} from a Maven dependency XML snippet.
*
* <pre>
* {@code
* <dependency>
* <groupId>groupId</groupId>
* <artifactId>artifactId</artifactId>
* <version>${some.version}</version>
* <classifier>classifier</classifier>
* <type>pom</type>
* </dependency>
* }
* </pre>
*/
public AddManagedDependencyRecipe create(String mavenDependencySnippet) {
try {
JsonNode jsonNode = getJsonNode(mavenDependencySnippet);
String groupId = getTextValue(jsonNode, "groupId");
String artifactId = getTextValue(jsonNode, "artifactId");
@Nullable
String version = getNullOrTextValue(jsonNode, "version");
@Nullable
String scope = getNullOrTextValue(jsonNode, "scope");
@Nullable
String classifier = getNullOrTextValue(jsonNode, "classifier");
@Nullable
String type = getNullOrTextValue(jsonNode, "type");
AddManagedDependencyRecipe addManagedDependency = new AddManagedDependencyRecipe(groupId, artifactId,
version, scope, type, classifier);
return addManagedDependency;
}
catch (JsonProcessingException ex) {
throw new RuntimeException(ex);
}
}
/**
* Create a {@link AddManagedDependencyRecipe} from a {@link Dependency}.
*/
public AddManagedDependencyRecipe create(Dependency dependency) {
String groupId = dependency.getGroupId();
String artifactId = dependency.getArtifactId();
String version = dependency.getVersion();
String scope = dependency.getScope();
String type = dependency.getType();
String classifier = dependency.getClassifier();
AddManagedDependencyRecipe addManagedDependency = new AddManagedDependencyRecipe(groupId, artifactId, version,
scope, type, classifier);
return addManagedDependency;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2021-2023 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.maven.AddPlugin;
/**
* @author Fabian Krüger
*/
public class AddPluginRecipeFactory extends AbstractRecipeFactory {
/**
* Create {@link AddPlugin} from Maven plugin XML snippet.
*/
public AddPlugin create(String buildPlugin) {
try {
JsonNode jsonNode = getJsonNode(buildPlugin);
String groupId = getTextValue(jsonNode, "groupId");
String artifactId = getTextValue(jsonNode, "artifactId");
@Nullable
String version = getNullOrTextValue(jsonNode, "version");
@Nullable
String configuration = getNullOrTextValue(jsonNode, "configuration");
@Nullable
String dependencies = getNullOrTextValue(jsonNode, "dependencies");
@Nullable
String executions = getNullOrTextValue(jsonNode, "executions");
@Nullable
String filePattern = getNullOrTextValue(jsonNode, "filePattern");
AddPlugin addPlugin = new AddPlugin(groupId, artifactId, version, configuration, dependencies, executions,
filePattern);
return addPlugin;
}
catch (JsonProcessingException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright 2021 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.xml.AddToTagVisitor;
import org.openrewrite.xml.XPathMatcher;
import org.openrewrite.xml.tree.Xml;
import org.openrewrite.xml.tree.Xml.Tag;
public class InjectTextMavenRepositoryRecipe extends Recipe {
private static final XPathMatcher REPOS_MATCHER = new XPathMatcher("/project/repositories");
private String text;
public InjectTextMavenRepositoryRecipe(String text) {
this.text = text;
}
@Override
public String getDisplayName() {
return "Add Repository";
}
@Override
public String getDescription() {
return getDisplayName();
}
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MavenIsoVisitor<ExecutionContext>() {
public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
Xml.Tag root = document.getRoot();
if (!root.getChild("repositories").isPresent()) {
document = (Xml.Document) (new AddToTagVisitor(root, Tag.build("<repositories/>")))
.visitNonNull(document, ctx, this.getCursor().getParentOrThrow());
}
return super.visitDocument(document, ctx);
}
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag repositories = super.visitTag(tag, ctx);
if (REPOS_MATCHER.matches(this.getCursor())) {
Xml.Tag repositoryTag = Tag.build(text);
repositories = (Xml.Tag) (new AddToTagVisitor(repositories, repositoryTag))
.visitNonNull(repositories, ctx, this.getCursor().getParentOrThrow());
this.maybeUpdateModel();
}
return repositories;
}
};
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2021-2023 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
/**
* @author Fabian Krüger
*/
public class RecipeCreationException extends RuntimeException {
public RecipeCreationException(String message) {
super(message);
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2021 the original author or 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
*
* https://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.springframework.ide.vscode.boot.java.copilot.recipe;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.openrewrite.Result;
import org.springframework.ide.vscode.boot.java.copilot.SpringCliException;
/**
* Utilities for recipe execution
*/
public final class RecipeUtils {
private RecipeUtils() {
}
public static void writeResults(String recipeName, Path path, List<Result> resultList) {
try {
for (Result result : resultList) {
try (BufferedWriter sourceFileWriter = Files.newBufferedWriter(path)) {
sourceFileWriter.write(result.getAfter().printAllTrimmed());
}
}
}
catch (IOException ex) {
throw new SpringCliException("Could not write recipe results to path = " + path, ex);
}
}
}

View File

@@ -1,128 +0,0 @@
package org.springframework.ide.vscode.boot.java.copilot.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.copilot.SpringCliException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public abstract class AbstractMavenReader {
private static final Logger logger = LoggerFactory.getLogger(AbstractMavenReader.class);
protected String sectionName;
/**
* Reads the Document and populates the provided array with individual dependency text
* values for each dependency element.
* @param document The Document to parse
* @param dependencies a list to populate with each dependency text
* @throws TransformerException if the element can't be converted to text
*/
protected void parseDocument(Document document, List<String> dependencies, String tagName)
throws TransformerException {
Element root = document.getDocumentElement();
NodeList nodeList = root.getElementsByTagName(tagName);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
for (int i = 0; i < nodeList.getLength(); i++) {
Node dependencyNode = nodeList.item(i);
if (dependencyNode.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) dependencyNode;
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(element), new StreamResult(writer));
String xml = writer.toString();
dependencies.add(xml);
}
}
}
protected Document buildDocument(ErrorHandler handler, InputStream stream)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder parser = dbf.newDocumentBuilder();
parser.setErrorHandler(handler);
return parser.parse(stream);
}
protected abstract String massageText(String text);
public String[] parseMavenSection(String text) {
String textToUse = massageText(text);
ErrorHandler errorHandler = new SimpleErrorHandler(logger);
List<String> dependencies = new ArrayList<>();
try {
InputStream inputStream = IOUtils.toInputStream(textToUse, StandardCharsets.UTF_8);
Document document = buildDocument(errorHandler, inputStream);
parseDocument(document, dependencies, this.sectionName);
}
catch (IOException ex) {
throw new SpringCliException("Cannot parse maven " + this.sectionName + " from string: " + text, ex);
}
catch (SAXException ex) {
throw new SpringCliException("Invalid XML in maven " + this.sectionName + " from string: " + text, ex);
}
catch (ParserConfigurationException ex) {
throw new SpringCliException("Internal error parsing maven " + this.sectionName + " from string:" + text,
ex);
}
catch (TransformerException ex) {
throw new SpringCliException("Internal error transforming Node to text from string:" + text, ex);
}
return dependencies.toArray(new String[0]);
}
protected class SimpleErrorHandler implements ErrorHandler {
private final Logger logger;
public SimpleErrorHandler(Logger logger) {
this.logger = logger;
}
@Override
public void warning(SAXParseException ex) throws SAXException {
logger.warn("Ignored XML validation warning", ex);
}
@Override
public void error(SAXParseException ex) throws SAXException {
throw ex;
}
@Override
public void fatalError(SAXParseException ex) throws SAXException {
throw ex;
}
}
}

View File

@@ -1,18 +0,0 @@
package org.springframework.ide.vscode.boot.java.copilot.util;
public class MavenBuildPluginReader extends AbstractMavenReader {
public MavenBuildPluginReader() {
this.sectionName = "plugin";
}
protected String massageText(String text) {
if (text.contains("<plugins>")) {
return text;
}
else {
return "<plugins>" + text + "</plugins>";
}
}
}

View File

@@ -1,18 +0,0 @@
package org.springframework.ide.vscode.boot.java.copilot.util;
public class MavenDependencyReader extends AbstractMavenReader {
public MavenDependencyReader() {
this.sectionName = "dependency";
}
protected String massageText(String text) {
if (text.contains("<dependencies>")) {
return text;
}
else {
return "<dependencies>" + text + "</dependencies>";
}
}
}

View File

@@ -1,18 +0,0 @@
package org.springframework.ide.vscode.boot.java.copilot.util;
public class MavenRepositoryReader extends AbstractMavenReader {
public MavenRepositoryReader() {
this.sectionName = "repository";
}
protected String massageText(String text) {
if (text.contains("<repositories>")) {
return text;
}
else {
return "<repositories>" + text + "</repositories>";
}
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.boot.java.copilot;
package org.springframework.ide.vscode.boot.java.copilot.util;
import java.io.File;
import java.io.FileInputStream;

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.boot.java.copilot;
package org.springframework.ide.vscode.boot.java.copilot.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@@ -7,7 +7,6 @@ import java.time.format.FormatStyle;
public class ResponseModifier {
public String modify(String response) {
System.out.println("In response modifier !!");
return modifyMsyqlDependency(modifyJavax(response));
}
@@ -53,8 +52,7 @@ public class ResponseModifier {
private String modifyMsyqlDependency(String response) {
if (!response.contains("<artifactId>mysql-connector-java</artifactId>")) {
return response;
}
else {
} else {
String s1 = response.replace("<groupId>mysql</groupId>", "<groupId>com.mysql</groupId>");
String s2 = s1.replace("<artifactId>mysql-connector-java</artifactId>",
"<artifactId>mysql-connector-j</artifactId>");

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.boot.java.copilot;
package org.springframework.ide.vscode.boot.java.copilot.util;
@SuppressWarnings("serial")
public class SpringCliException extends RuntimeException {

View File

@@ -1,4 +1,4 @@
import { Uri, workspace, window, commands } from "vscode";
import { Uri, workspace, window, commands, ProgressLocation } from "vscode";
import { getTargetGuideMardown, readResponseFromFile } from "./util";
import { createConverter } from "vscode-languageclient/lib/common/protocolConverter";
import fs from "fs";
@@ -14,21 +14,29 @@ export async function applyLspEdit(uri: Uri) {
uri = await getTargetGuideMardown();
}
const fileContent = (await readResponseFromFile(uri)).toString();
const lspEdit = await commands.executeCommand("sts/copilot/agent/lspEdits", uri.toString(), path.dirname(uri.fsPath), fileContent);
const workspaceEdit = await CONVERTER.asWorkspaceEdit(lspEdit);
window.withProgress({
location: ProgressLocation.Window,
title: "Copilot agent",
cancellable: true
}, async (progress, cancellation) => {
progress.report({ message: "applying edits..." });
const fileContent = (await readResponseFromFile(uri)).toString();
const lspEdit = await commands.executeCommand("sts/copilot/agent/lspEdits", uri.toString(), path.dirname(uri.fsPath), fileContent);
const workspaceEdit = await CONVERTER.asWorkspaceEdit(lspEdit);
await Promise.all(workspaceEdit.entries().map(async ([uri, edits]) => {
console.log(edits);
if (fs.existsSync(uri.fsPath)) {
const doc = await workspace.openTextDocument(uri.fsPath);
await window.showTextDocument(doc);
}
}));
await Promise.all(workspaceEdit.entries().map(async ([uri, edits]) => {
console.log(edits);
if (fs.existsSync(uri.fsPath)) {
const doc = await workspace.openTextDocument(uri.fsPath);
await window.showTextDocument(doc);
}
}));
return await workspace.applyEdit(workspaceEdit, {
isRefactoring: true
});
return await workspace.applyEdit(workspaceEdit, {
isRefactoring: true
});
});
} catch (error) {
if (error !== CANCELLED) {
window.showErrorMessage(error);

View File

@@ -86,7 +86,7 @@ export default class SpringBootChatAgent {
stream.markdown(chatResponse);
stream.button({
command: 'vscode-spring-boot.agent.apply',
title: l10n.t('Preview Changes')
title: l10n.t('Apply Changes')
});
return { metadata: { command: '' } };
}

View File

@@ -1,121 +0,0 @@
// import { ProgressLocation, Uri, window, workspace } from "vscode";
// import cp from "child_process";
// import * as vscode from 'vscode';
// import { homedir } from "os";
// import { getWorkspaceRoot, getWorkspaceRootPath } from "./util";
// import path from "path";
// import { WorkspaceEdit } from "vscode-languageclient";
// export const CANCELLED = "Cancelled";
// export class SpringCli {
// get executable(): string {
// return workspace.getConfiguration("spring-cli").get("executable") || "spring";
// }
// guideLspEdit(uri: Uri, cwd?: string): Promise<WorkspaceEdit> {
// const args = [
// "guide",
// "apply",
// "--lsp-edit",
// "--file",
// uri.fsPath
// ];
// return this.fetchJson("Applying guide", uri.fsPath, args, cwd || path.dirname(uri.fsPath), true);
// }
// enhanceResponse(uri: Uri, cwd: string): Thenable<string> {
// const args = [
// "ai",
// "enhance-response",
// "--file",
// uri.fsPath
// ];
// return this.exec("Spring cli ai", "Enhance response", args, cwd);
// }
// private async executeCommand(args: string[], cwd?: string): Promise<string> {
// const processOpts = { cwd: cwd || (await getWorkspaceRoot())?.fsPath || homedir() };
// const process = this.executable.endsWith(".jar") ? await cp.exec(`java -jar ${this.executable} ${args.join(" ")}`, processOpts) : await cp.exec(`${this.executable} ${args.join(" ")}`, processOpts);
// const dataChunks: string[] = [];
// process.stdout.on("data", s => dataChunks.push(s));
// return new Promise<string>((resolve, reject) => {
// process.on("exit", (code) => {
// if (code) {
// reject(`Failed to execute command: ${dataChunks.join()}`);
// } else {
// resolve(dataChunks.join());
// }
// });
// });
// }
// private async exec<T>(title: string, message: string, args: string[], cwd?: string): Promise<T> {
// return vscode.window.withProgress({
// location: vscode.ProgressLocation.Window,
// cancellable: true,
// title,
// }, async (progress, cancellation) => {
// if (message) {
// progress.report({ message });
// }
// return new Promise<T>(async (resolve, reject) => {
// if (cancellation.isCancellationRequested) {
// reject("Cancelled");
// }
// try {
// const output: string = await this.executeCommand(args, cwd);
// resolve(output as T);
// } catch (error) {
// console.error(`Error: ${error}`);
// reject(error);
// }
// });
// });
// }
// private async fetchJson<T>(title: string, message: string, args: string[], cwd?: string, omitJsonParam?: boolean): Promise<T> {
// return window.withProgress({
// location: ProgressLocation.Window,
// cancellable: true,
// title
// }, (progress, cancellation) => {
// if (message) {
// progress.report({ message });
// }
// return new Promise<T>(async (resolve, reject) => {
// if (cancellation.isCancellationRequested) {
// reject(CANCELLED);
// }
// const processOpts = { cwd: cwd || getWorkspaceRootPath()?.fsPath || homedir() };
// const process = this.executable.endsWith(".jar") ? await cp.exec(`java -jar ${this.executable} ${args.join(" ")}`, processOpts) : await cp.exec(`${this.executable} ${args.join(" ")} ${omitJsonParam ? "" : "--json"}`, processOpts);
// cancellation.onCancellationRequested(() => process.kill());
// const dataChunks: string[] = [];
// process.stdout.on("data", s => dataChunks.push(s));
// process.on("exit", (code) => {
// if (code) {
// if (cancellation.isCancellationRequested) {
// reject(CANCELLED);
// } else {
// reject(`Failed to fetch data: ${dataChunks.join()}`);
// }
// } else {
// try {
// resolve(JSON.parse(dataChunks.join()) as T);
// } catch (error) {
// reject(error);
// }
// }
// });
// });
// });
// }
// }