diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml index cba608063..0df5b2fbe 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml @@ -344,11 +344,11 @@ diff --git a/headless-services/commons/commons-rewrite-test/src/main/java/org/springframework/rewrite/test/HelloMethodRenameProblemDescriptor.java b/headless-services/commons/commons-rewrite-test/src/main/java/org/springframework/rewrite/test/HelloMethodRenameProblemDescriptor.java index bc992f09b..6adf43e8f 100644 --- a/headless-services/commons/commons-rewrite-test/src/main/java/org/springframework/rewrite/test/HelloMethodRenameProblemDescriptor.java +++ b/headless-services/commons/commons-rewrite-test/src/main/java/org/springframework/rewrite/test/HelloMethodRenameProblemDescriptor.java @@ -10,7 +10,10 @@ *******************************************************************************/ package org.springframework.rewrite.test; +import java.util.List; + import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; @@ -26,23 +29,12 @@ import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDes import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; public class HelloMethodRenameProblemDescriptor implements RecipeSpringJavaProblemDescriptor { - @Override - public String getRecipeId() { - return "org.springframework.rewrite.test.HelloMethodRenameRecipe"; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel("Switch hello method into bye", s); - } - - @Override - public RecipeScope[] getScopes() { - return RecipeScope.values(); - } + private static final String LABEL = "Switch hello method into bye"; + private static final String RECIPE_ID = "org.springframework.rewrite.test.HelloMethodRenameRecipe"; @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { @@ -52,7 +44,17 @@ public class HelloMethodRenameProblemDescriptor implements RecipeSpringJavaProbl public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, ExecutionContext p) { MethodDeclaration m = super.visitMethodDeclaration(method, p); if ("hello".equals(method.getSimpleName())) { - FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()).withRecipeId(getRecipeId()).withScope(m.getMarkers().findFirst(Range.class).get()); + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); + FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()) + .withFixes( + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.NODE)) + .withRecipeScope(RecipeScope.NODE) + .withRangeScope(m.getMarkers().findFirst(Range.class).get()), + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE)) + .withRecipeScope(RecipeScope.FILE), + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT) + ); m = m.withName(m.getName().withMarkers(m.getName().getMarkers().add(marker))); } return m; diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/config/RecipeCodeActionDescriptor.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/config/RecipeCodeActionDescriptor.java index 252ae5b50..632f2073b 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/config/RecipeCodeActionDescriptor.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/config/RecipeCodeActionDescriptor.java @@ -21,12 +21,6 @@ public interface RecipeCodeActionDescriptor { return getClass().getName(); } - String getRecipeId(); - - String getLabel(RecipeScope s); - - RecipeScope[] getScopes(); - JavaVisitor getMarkerVisitor(ApplicationContext applicationContext); boolean isApplicable(IJavaProject project); diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixAssistMarker.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixAssistMarker.java index 08e05b91c..cab32e755 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixAssistMarker.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixAssistMarker.java @@ -1,24 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ package org.springframework.ide.vscode.commons.rewrite.java; -import java.util.Collections; -import java.util.Map; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.UUID; import org.openrewrite.marker.Marker; -import org.openrewrite.marker.Range; public class FixAssistMarker implements Marker { private UUID id; - private Range scope; - - private String recipeId; - private String descriptorId; - private Map parameters = Collections.emptyMap(); + private List fixes = new ArrayList<>();; public FixAssistMarker(UUID id, String descriptorId) { super(); @@ -38,40 +44,27 @@ public class FixAssistMarker implements Marker { return this; } - public FixAssistMarker withScope(Range scope) { - this.scope = scope; - return this; - } - - public Range getScope() { - return scope; - } - - public FixAssistMarker withRecipeId(String recipeId) { - this.recipeId = recipeId; - return this; - } - - public String getRecipeId() { - return recipeId; - } - - public FixAssistMarker withParameters(Map parameters) { - this.parameters = parameters; - return this; - } - - public Map getParameters() { - return parameters; - } - public String getDescriptorId() { return descriptorId; } + + public FixAssistMarker withFix(FixDescriptor f) { + fixes.add(f); + return this; + } + + public FixAssistMarker withFixes(FixDescriptor... fixes) { + this.fixes.addAll(Arrays.asList(fixes)); + return this; + } + + public List getFixes() { + return fixes; + } @Override public int hashCode() { - return Objects.hash(descriptorId, id, parameters, recipeId, scope); + return Objects.hash(descriptorId, fixes, id); } @Override @@ -83,10 +76,9 @@ public class FixAssistMarker implements Marker { if (getClass() != obj.getClass()) return false; FixAssistMarker other = (FixAssistMarker) obj; - return Objects.equals(descriptorId, other.descriptorId) && Objects.equals(id, other.id) - && Objects.equals(parameters, other.parameters) && Objects.equals(recipeId, other.recipeId) - && Objects.equals(scope, other.scope); + return Objects.equals(descriptorId, other.descriptorId) && Objects.equals(fixes, other.fixes) + && Objects.equals(id, other.id); } - + } diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixDescriptor.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixDescriptor.java new file mode 100644 index 000000000..d0e3ebf92 --- /dev/null +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/FixDescriptor.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.rewrite.java; + +import java.util.List; +import java.util.Map; + +import org.openrewrite.marker.Range; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; + +final public class FixDescriptor { + + private String recipeId; + + private RecipeScope recipeScope; + + private Range rangeScope; + + private List docUris; + + private Map parameters; + + private String label; + + public FixDescriptor(String recipeId, List docUris, String label) { + this.recipeId = recipeId; + this.docUris = docUris; + this.label = label; + } + + public FixDescriptor withRecipeScope(RecipeScope recipeScope) { + this.recipeScope = recipeScope; + return this; + } + + public FixDescriptor withRangeScope(Range rangeScope) { + this.rangeScope = rangeScope; + return this; + } + + public FixDescriptor withParameters(Map parameters) { + this.parameters = parameters; + return this; + } + + public String getRecipeId() { + return recipeId; + } + + public RecipeScope getRecipeScope() { + return recipeScope; + } + + public Range getRangeScope() { + return rangeScope; + } + + public List getDocUris() { + return docUris; + } + + public Map getParameters() { + return parameters; + } + + public String getLabel() { + return label; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java index b5652eee6..828ba25b6 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java @@ -13,9 +13,7 @@ package org.springframework.ide.vscode.boot.java.rewrite; import java.util.List; import org.springframework.ide.vscode.boot.java.rewrite.codeaction.AutowiredFieldIntoConstructorParameterCodeAction; -import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNotPublicCodeAction; import org.springframework.ide.vscode.boot.java.rewrite.codeaction.NoRequestMappingAnnotationCodeAction; -import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanMethodNotPublicProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanPostProcessingIgnoreInAotProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoAutowiredOnConstructorProblem; @@ -32,9 +30,7 @@ public class BootCodeActionRepository extends CodeActionRepository { public List getCodeActionDescriptors() { return List.of( new AutowiredFieldIntoConstructorParameterCodeAction(), - new BeanMethodsNotPublicCodeAction(), - new NoRequestMappingAnnotationCodeAction(), - new UnnecessarySpringExtensionCodeAction() + new NoRequestMappingAnnotationCodeAction() ); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java index 9712c3968..016c4009c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java @@ -12,10 +12,8 @@ package org.springframework.ide.vscode.boot.java.rewrite; import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; import org.eclipse.lsp4j.CodeAction; @@ -41,9 +39,8 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.util.LspClient; import org.springframework.ide.vscode.commons.languageserver.util.LspClient.Client; import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.IRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -103,7 +100,6 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler { List descriptors = recipeRepo.getCodeActionRecipeDescriptors().stream() // If Recipe not present - don't show quick assist as it won't be handled without the Rewrite recipe present - .filter(d -> recipeRepo.getRecipe(d.getRecipeId()).isPresent()) .filter(d -> d.isApplicable(project)) .collect(Collectors.toList()); @@ -151,34 +147,20 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler { private CodeAction[] createCodeActions(IDocument doc, FixAssistMarker m, J astNode) { if (astNode != null) { - Range range = astNode.getMarkers().findFirst(Range.class).orElse(null); - RecipeCodeActionDescriptor descriptor = recipeRepo.getCodeActionRecipeDescriptor(m.getRecipeId()); - if (descriptor != null && descriptor.getScopes() != null) { - // Fix descriptor code actions may have the overlapping scopes with assist descriptors. Quick fixes are provided separately hence overlapping scopes will produces duplicates quick assist. - // Therefore, need to compute recipe scopes that don't overlap with quick fix descriptor recipe scopes. - RecipeSpringJavaProblemDescriptor fixDescriptor = recipeRepo.getProblemRecipeDescriptor(m.getDescriptorId()); - return Arrays.stream(descriptor.getScopes()) - .filter(s -> fixDescriptor == null || !Arrays.asList(fixDescriptor.getScopes()).contains(s)) - .map(s -> createCodeActionFromScope(doc, descriptor, s, m, range)) - .filter(Objects::nonNull) - .toArray(CodeAction[]::new); - } + return m.getFixes().stream() + .filter(d -> recipeRepo.getRecipe(d.getRecipeId()) != null) + .map(d -> createCodeActionFromScope(doc, d)) + .toArray(CodeAction[]::new); } return new CodeAction[0]; } - private CodeAction createCodeActionFromScope(IDocument doc, RecipeCodeActionDescriptor descriptor, - RecipeScope s, FixAssistMarker m, Range range) { + private CodeAction createCodeActionFromScope(IDocument doc, + FixDescriptor d) { CodeAction ca = new CodeAction(); ca.setKind(CodeActionKind.Refactor); - ca.setTitle(descriptor.getLabel(s)); - ca.setData(new RewriteRefactorings.Data( - m.getRecipeId(), - doc.getUri(), - s, - m.getScope() == null ? null : m.getScope(), - m.getParameters() == null ? Collections.emptyMap() : m.getParameters() - )); + ca.setTitle(d.getLabel()); + ca.setData(d); return ca; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java index dfec031cf..f5c71a62e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java @@ -37,7 +37,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler; -import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings.Data; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData; @@ -47,9 +46,9 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemC import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -115,13 +114,15 @@ public class RewriteReconciler implements JavaReconciler { ProblemType problemType = recipeFixDescriptor.getProblemType(); ReconcileProblemImpl problem = new ReconcileProblemImpl(problemType, problemType.getLabel(), range.getStart().getOffset(), range.getEnd().getOffset() - range.getStart().getOffset()); QuickfixType quickfixType = quickfixRegistry.getQuickfixType(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX); - if (quickfixType != null && m.getRecipeId() != null && recipeRepo.getRecipe(recipeFixDescriptor.getRecipeId()).isPresent()) { - for (RecipeScope s : recipeFixDescriptor.getScopes()) { - problem.addQuickfix(new QuickfixData<>( - quickfixType, - new Data(m.getRecipeId(), doc.getUri(), s, m.getScope(), m.getParameters()), - recipeFixDescriptor.getLabel(s) - )); + if (quickfixType != null) { + for (FixDescriptor f : m.getFixes()) { + if (recipeRepo.getRecipe(f.getRecipeId()) != null) { + problem.addQuickfix(new QuickfixData<>( + quickfixType, + f, + f.getLabel() + )); + } } } return problem; @@ -137,19 +138,21 @@ public class RewriteReconciler implements JavaReconciler { List descriptors = getProblemRecipeDescriptors(project); JavaParser javaParser = ORAstUtils.createJavaParser(project); - List cus = ORAstUtils.parseInputs(javaParser, docs.stream().map(d -> new Parser.Input(Path.of(d.getUri()), () -> { - return new ByteArrayInputStream(d.get().getBytes()); - })).collect(Collectors.toList())); - - if (!descriptors.isEmpty()) { + if (javaParser != null) { + List cus = ORAstUtils.parseInputs(javaParser, docs.stream().map(d -> new Parser.Input(Path.of(d.getUri()), () -> { + return new ByteArrayInputStream(d.get().getBytes()); + })).collect(Collectors.toList())); - for(int i = 0; i < cus.size(); i++) { - final IDocument doc = docs.get(i); - List problems = new ArrayList<>(); - collectProblems(descriptors, doc, cus.get(i), problems::add); - if (!problems.isEmpty()) { - allProblems.put(doc, problems); - } + if (!descriptors.isEmpty()) { + + for(int i = 0; i < cus.size(); i++) { + final IDocument doc = docs.get(i); + List problems = new ArrayList<>(); + collectProblems(descriptors, doc, cus.get(i), problems::add); + if (!problems.isEmpty()) { + allProblems.put(doc, problems); + } + } } } } catch (Exception e) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java index 332155809..dc78f6adb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java @@ -13,8 +13,8 @@ package org.springframework.ide.vscode.boot.java.rewrite; import java.lang.reflect.Field; import java.net.URI; import java.util.List; -import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -42,6 +42,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.CodeActionReso import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.rewrite.ORDocUtils; import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -107,8 +108,8 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler } public WorkspaceEdit createEdit(JsonElement o) { - Data data = gson.fromJson(o, Data.class); - if (data != null && data.id != null) { + FixDescriptor data = gson.fromJson(o, FixDescriptor.class); + if (data != null && data.getRecipeId() != null) { return perform(data); } return null; @@ -133,32 +134,29 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler return workspaceEdit; } - private WorkspaceEdit perform(Data data) { - Optional project = projectFinder.find(new TextDocumentIdentifier(data.docUri)); + private WorkspaceEdit perform(FixDescriptor data) { + Optional project = projectFinder.find(new TextDocumentIdentifier(data.getDocUris().get(0))); if (project.isPresent()) { - boolean projectWide = data.recipeScope == RecipeScope.PROJECT; + boolean projectWide = data.getRecipeScope() == RecipeScope.PROJECT; Recipe r = createRecipe(data); if (projectWide) { return applyRecipe(r, project.get(), ORAstUtils.parse(documents, project.get())); } else { - CompilationUnit cu = cuCache.getCU(project.get(), URI.create(data.docUri)); - if (cu == null) { - throw new IllegalStateException("Cannot parse Java file: " + data.docUri); - } - return applyRecipe(r, project.get(), List.of(cu)); + List cus = data.getDocUris().stream().map(docUri -> cuCache.getCU(project.get(), URI.create(docUri))).filter(Objects::nonNull).collect(Collectors.toList()); + return applyRecipe(r, project.get(), cus); } } return null; } - private Recipe createRecipe(Data d) { - Recipe r = recipeRepo.getRecipe(d.id).orElse(null); + private Recipe createRecipe(FixDescriptor d) { + Recipe r = recipeRepo.getRecipe(d.getRecipeId()).orElse(null); if (!(r instanceof DeclarativeRecipe)) { r = RecipeIntrospectionUtils.constructRecipe(r.getClass()); } - if (d.params != null) { - for (Entry entry : d.params.entrySet()) { + if (d.getParameters() != null) { + for (Entry entry : d.getParameters().entrySet()) { try { Field f = r.getClass().getDeclaredField(entry.getKey()); f.setAccessible(true); @@ -168,15 +166,15 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler } } } - if (d.recipeScope == RecipeScope.NODE) { - if (d.scope == null) { + if (d.getRecipeScope() == RecipeScope.NODE) { + if (d.getRangeScope() == null) { throw new IllegalArgumentException("Missing scope AST node!"); } else { r = ORAstUtils.nodeRecipe(r, j -> { if (j != null) { Range range = j.getMarkers().findFirst(Range.class).orElse(null); if (range != null) { - return d.scope.getStart().getOffset() <= range.getStart().getOffset() && range.getEnd().getOffset() <= d.scope.getEnd().getOffset(); + return d.getRangeScope().getStart().getOffset() <= range.getStart().getOffset() && range.getEnd().getOffset() <= d.getRangeScope().getEnd().getOffset(); } } return false; @@ -185,21 +183,4 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler } return r; } - - public static class Data { - public String id; - public String docUri; - public RecipeScope recipeScope; - public Range scope; - public Map params; - public Data(String id, String docUri, RecipeScope recipeScope, Range scope, Map params) { - this.id = id; - this.docUri = docUri; - this.recipeScope = recipeScope; - this.scope = scope; - this.params = params; - } - - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java index 541396f29..5a8c605e5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java @@ -19,6 +19,7 @@ import java.util.stream.Collectors; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; @@ -36,6 +37,7 @@ import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDes import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.java.AnnotationHierarchies; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils; public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeCodeActionDescriptor { @@ -44,21 +46,6 @@ public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeC private static final String ID = "org.springframework.ide.vscode.commons.rewrite.java.ConvertAutowiredFieldIntoConstructorParameter"; private static final String AUTOWIRED = "org.springframework.beans.factory.annotation.Autowired"; - @Override - public String getRecipeId() { - return ID; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel(LABEL, s); - } - - @Override - public RecipeScope[] getScopes() { - return new RecipeScope[] { RecipeScope.NODE }; - } - @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor<>() { @@ -76,10 +63,14 @@ public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeC if (fqType != null && isApplicableType(fqType)) { List constructors = ORAstUtils.getMethods(classDeclaration).stream().filter(c -> c.isConstructor()).limit(2).collect(Collectors.toList()); String fieldName = multiVariable.getVariables().get(0).getSimpleName(); + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()) - .withRecipeId(getRecipeId()) - .withScope(classDeclaration.getMarkers().findFirst(Range.class).get()) - .withParameters(Map.of("classFqName", fqType.getFullyQualifiedName(), "fieldName", fieldName)); + .withFix( + new FixDescriptor(ID, List.of(uri), LABEL) + .withRangeScope(classDeclaration.getMarkers().findFirst(Range.class).get()) + .withParameters(Map.of("classFqName", fqType.getFullyQualifiedName(), "fieldName", fieldName)) + .withRecipeScope(RecipeScope.NODE) + ); if (constructors.size() == 0) { m = m.withMarkers(m.getMarkers().add(marker)); } else if (constructors.size() == 1 && !AutowiredFieldIntoConstructorParameterVisitor.isConstructorInitializingField(constructors.get(0), fieldName)) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/BeanMethodsNotPublicCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/BeanMethodsNotPublicCodeAction.java deleted file mode 100644 index 409f45da1..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/BeanMethodsNotPublicCodeAction.java +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 VMware, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VMware, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.rewrite.codeaction; - -import org.openrewrite.ExecutionContext; -import org.openrewrite.Tree; -import org.openrewrite.internal.ListUtils; -import org.openrewrite.java.AnnotationMatcher; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.TypeUtils; -import org.openrewrite.marker.Range; -import org.springframework.context.ApplicationContext; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.SpringProjectUtil; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; -import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; - -public class BeanMethodsNotPublicCodeAction implements RecipeCodeActionDescriptor { - - private static final String ID = "org.openrewrite.java.spring.BeanMethodsNotPublic"; - - private static final String LABEL = "Remove 'public' from @Bean method"; - - private static final AnnotationMatcher BEAN_ANNOTATION_MATCHER = new AnnotationMatcher("@org.springframework.context.annotation.Bean"); - - @Override - public String getRecipeId() { - return ID; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel(LABEL, s); - } - - @Override - public RecipeScope[] getScopes() { - return RecipeScope.values(); - } - - @Override - public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { - return new JavaIsoVisitor() { - - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) { - J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext); - - if (m.getAllAnnotations().stream().anyMatch(BEAN_ANNOTATION_MATCHER::matches) - && Boolean.FALSE.equals(TypeUtils.isOverride(method.getMethodType()))) { - // mark public modifier - FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) - .withRecipeId(ID) - .withScope(m.getMarkers().findFirst(Range.class).get()); - m = m.withModifiers(ListUtils.map(m.getModifiers(), modifier -> { - if (modifier.getType() == J.Modifier.Type.Public) { - return modifier.withMarkers(modifier.getMarkers().add(fixAssistMarker)); - } - return modifier; - })); - } - return m; - } - - }; - } - - @Override - public boolean isApplicable(IJavaProject project) { - return SpringProjectUtil.getDependencyVersion(project, SpringProjectUtil.SPRING_BOOT) != null; - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/NoRequestMappingAnnotationCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/NoRequestMappingAnnotationCodeAction.java index 97824e898..9692fc9d1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/NoRequestMappingAnnotationCodeAction.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/NoRequestMappingAnnotationCodeAction.java @@ -12,7 +12,10 @@ package org.springframework.ide.vscode.boot.java.rewrite.codeaction; import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; +import java.util.List; + import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.AnnotationMatcher; import org.openrewrite.java.JavaIsoVisitor; @@ -24,6 +27,7 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; public class NoRequestMappingAnnotationCodeAction implements RecipeCodeActionDescriptor { @@ -31,21 +35,6 @@ public class NoRequestMappingAnnotationCodeAction implements RecipeCodeActionDes private static final String ID = "org.openrewrite.java.spring.NoRequestMappingAnnotation"; private static final AnnotationMatcher REQUEST_MAPPING_ANNOTATION_MATCHER = new AnnotationMatcher("@org.springframework.web.bind.annotation.RequestMapping"); - @Override - public String getRecipeId() { - return ID; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel(LABEL, s); - } - - @Override - public RecipeScope[] getScopes() { - return RecipeScope.values(); - } - @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @@ -53,9 +42,17 @@ public class NoRequestMappingAnnotationCodeAction implements RecipeCodeActionDes public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { J.Annotation a = super.visitAnnotation(annotation, ctx); if (REQUEST_MAPPING_ANNOTATION_MATCHER.matches(a) && getCursor().getParentOrThrow().getValue() instanceof J.MethodDeclaration) { + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) - .withRecipeId(getRecipeId()) - .withScope(a.getMarkers().findFirst(Range.class).get()); + .withFixes( + new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.NODE)) + .withRangeScope(a.getMarkers().findFirst(Range.class).get()) + .withRecipeScope(RecipeScope.NODE), + new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE)) + .withRecipeScope(RecipeScope.FILE), + new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT) + ); a = a.withMarkers(a.getMarkers().add(fixAssistMarker)); } return a; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/UnnecessarySpringExtensionCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/UnnecessarySpringExtensionCodeAction.java deleted file mode 100644 index 8da1fd82c..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/UnnecessarySpringExtensionCodeAction.java +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 VMware, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VMware, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.rewrite.codeaction; - -import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; - -import java.util.Arrays; -import java.util.List; - -import org.openrewrite.ExecutionContext; -import org.openrewrite.Tree; -import org.openrewrite.internal.ListUtils; -import org.openrewrite.java.AnnotationMatcher; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.tree.J.ClassDeclaration; -import org.openrewrite.java.tree.JavaType.FullyQualified; -import org.openrewrite.java.tree.TypeUtils; -import org.openrewrite.marker.Range; -import org.springframework.context.ApplicationContext; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; -import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; - -public class UnnecessarySpringExtensionCodeAction implements RecipeCodeActionDescriptor { - - private static final String LABEL = "Remove unnecessary @SpringExtension"; - private static final String ID = "org.openrewrite.java.spring.boot2.UnnecessarySpringExtension"; - private static final List SPRING_BOOT_TEST_ANNOTATIONS = Arrays.asList( - "org.springframework.boot.test.context.SpringBootTest", - "org.springframework.boot.test.autoconfigure.jdbc.JdbcTest", - "org.springframework.boot.test.autoconfigure.web.client.RestClientTest", - "org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest", - "org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest", - "org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest", - "org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest", - "org.springframework.boot.test.autoconfigure.jooq.JooqTest", - "org.springframework.boot.test.autoconfigure.json.JsonTest", - "org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest", - "org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest", - "org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest", - "org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest", - "org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest", - "org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest", - "org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest" - ); - private static final AnnotationMatcher SPRING_EXTENSION_ANNOTATIN_MATCHER = new AnnotationMatcher("@org.junit.jupiter.api.extension.ExtendWith(org.springframework.test.context.junit.jupiter.SpringExtension.class)"); - - @Override - public String getRecipeId() { - return ID; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel(LABEL, s); - } - - @Override - public RecipeScope[] getScopes() { - return new RecipeScope[] { RecipeScope.PROJECT }; - } - - @Override - public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { - return new JavaIsoVisitor<>() { - - @Override - public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) { - ClassDeclaration c = super.visitClassDeclaration(classDecl, p); - if (c.getLeadingAnnotations().stream().anyMatch(a -> { - FullyQualified fq = TypeUtils.asFullyQualified(a.getType()); - return fq != null && SPRING_BOOT_TEST_ANNOTATIONS.contains(fq.getFullyQualifiedName()); - })) { - Range range = c.getMarkers().findFirst(Range.class).get(); - c = c.withLeadingAnnotations(ListUtils.map(c.getLeadingAnnotations(), a -> { - if (SPRING_EXTENSION_ANNOTATIN_MATCHER.matches(a)) { - return a.withMarkers(a.getMarkers().add(new FixAssistMarker(Tree.randomId(), getId()).withRecipeId(ID).withScope(range))); - } - return a; - })); - } - return c; - } - - }; - } - - @Override - public boolean isApplicable(IJavaProject project) { - return springBootVersionGreaterOrEqual(2, 1, 0).test(project); - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanMethodNotPublicProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanMethodNotPublicProblem.java index 2a6996c39..bddb35dcf 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanMethodNotPublicProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanMethodNotPublicProblem.java @@ -10,16 +10,75 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.rewrite.reconcile; +import java.util.List; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; +import org.openrewrite.Tree; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.AnnotationMatcher; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.marker.Range; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; -import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNotPublicCodeAction; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; +import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; -public class BeanMethodNotPublicProblem extends BeanMethodsNotPublicCodeAction implements RecipeSpringJavaProblemDescriptor { +public class BeanMethodNotPublicProblem implements RecipeSpringJavaProblemDescriptor { + + private static final String ID = "org.openrewrite.java.spring.BeanMethodsNotPublic"; + + private static final String LABEL = "Remove 'public' from @Bean method"; + + private static final AnnotationMatcher BEAN_ANNOTATION_MATCHER = new AnnotationMatcher("@org.springframework.context.annotation.Bean"); + + @Override + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { + return new JavaIsoVisitor() { + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext); + + if (m.getAllAnnotations().stream().anyMatch(BEAN_ANNOTATION_MATCHER::matches) + && Boolean.FALSE.equals(TypeUtils.isOverride(method.getMethodType()))) { + // mark public modifier + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); + FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) + .withFixes( + new FixDescriptor(ID, List.of(uri), LABEL) + .withRangeScope(m.getMarkers().findFirst(Range.class).get()) + .withRecipeScope(RecipeScope.NODE), + new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE)) + .withRecipeScope(RecipeScope.FILE), + new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT) + + ); + m = m.withModifiers(ListUtils.map(m.getModifiers(), modifier -> { + if (modifier.getType() == J.Modifier.Type.Public) { + return modifier.withMarkers(modifier.getMarkers().add(fixAssistMarker)); + } + return modifier; + })); + } + return m; + } + + }; + } @Override - public RecipeScope[] getScopes() { - return new RecipeScope[] { RecipeScope.NODE }; + public boolean isApplicable(IJavaProject project) { + return SpringProjectUtil.getDependencyVersion(project, SpringProjectUtil.SPRING_BOOT) != null; } @Override diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanPostProcessingIgnoreInAotProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanPostProcessingIgnoreInAotProblem.java index 41e30acf6..55ff31298 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanPostProcessingIgnoreInAotProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/BeanPostProcessingIgnoreInAotProblem.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.stream.Collectors; import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; @@ -31,26 +32,13 @@ import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.BeanPostProcessingIgnoreInAot; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; public class BeanPostProcessingIgnoreInAotProblem implements RecipeSpringJavaProblemDescriptor { + private static final String RECIPE_ID = "org.springframework.ide.vscode.commons.rewrite.java.BeanPostProcessingIgnoreInAot"; private static final String LABEL = "Add method 'isBeanExcludedFromAotProcessing' that returns 'false'"; - @Override - public String getRecipeId() { - return "org.springframework.ide.vscode.commons.rewrite.java.BeanPostProcessingIgnoreInAot"; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel(LABEL, s); - } - - @Override - public RecipeScope[] getScopes() { - return RecipeScope.values(); - } - @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @@ -63,9 +51,17 @@ public class BeanPostProcessingIgnoreInAotProblem implements RecipeSpringJavaPro .filter(MethodDeclaration.class::isInstance).map(MethodDeclaration.class::cast) .filter(BeanPostProcessingIgnoreInAot::isApplicableMethod) .collect(Collectors.toList()); + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()) - .withRecipeId(getRecipeId()) - .withScope(classDecl.getMarkers().findFirst(Range.class).orElse(null)); + .withFixes( + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.NODE)) + .withRangeScope(classDecl.getMarkers().findFirst(Range.class).orElse(null)) + .withRecipeScope(RecipeScope.NODE), + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE)) + .withRecipeScope(RecipeScope.FILE), + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT) + ); if (methods.isEmpty()) { // Didn't find a method. Default implementation return true therefore mark it. c = c.withName(c.getName().withMarkers(c.getName().getMarkers().add(marker))); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoAutowiredOnConstructorProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoAutowiredOnConstructorProblem.java index 4ea58a918..69d830a8a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoAutowiredOnConstructorProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoAutowiredOnConstructorProblem.java @@ -12,7 +12,10 @@ package org.springframework.ide.vscode.boot.java.rewrite.reconcile; import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; +import java.util.List; + import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; @@ -32,27 +35,13 @@ import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.AnnotationHierarchies; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; public class NoAutowiredOnConstructorProblem implements RecipeSpringJavaProblemDescriptor { private static final String ID = "org.openrewrite.java.spring.NoAutowiredOnConstructor"; private static final String LABEL = "Remove Unnecessary @Autowired"; - @Override - public String getRecipeId() { - return ID; - } - - @Override - public String getLabel(RecipeScope s) { - return LABEL; - } - - @Override - public RecipeScope[] getScopes() { - return new RecipeScope[] { RecipeScope.NODE }; - } - @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @@ -77,9 +66,13 @@ public class NoAutowiredOnConstructorProblem implements RecipeSpringJavaProblemD return s; } MethodDeclaration constructor = (MethodDeclaration) s; + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) - .withRecipeId(ID) - .withScope(getCursor().firstEnclosing(ClassDeclaration.class).getMarkers().findFirst(Range.class).get()); + .withFix( + new FixDescriptor(ID, List.of(uri), LABEL) + .withRecipeScope(RecipeScope.NODE) + .withRangeScope(getCursor().firstEnclosing(ClassDeclaration.class).getMarkers().findFirst(Range.class).get()) + ); constructor = constructor.withLeadingAnnotations(ListUtils.map(constructor.getLeadingAnnotations(), a -> { if (TypeUtils.isOfClassType(a.getType(), Annotations.AUTOWIRED)) { a = a.withMarkers(a.getMarkers().add(fixAssistMarker)); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NotRegisteredBeansProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NotRegisteredBeansProblem.java index 874d6ac6a..27634e917 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NotRegisteredBeansProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NotRegisteredBeansProblem.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ package org.springframework.ide.vscode.boot.java.rewrite.reconcile; import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; @@ -13,8 +23,6 @@ import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J.ClassDeclaration; import org.openrewrite.java.tree.JavaType.FullyQualified; import org.openrewrite.java.tree.TypeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType; @@ -22,38 +30,17 @@ import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformatio import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; public class NotRegisteredBeansProblem implements RecipeSpringJavaProblemDescriptor { - private static final Logger log = LoggerFactory.getLogger(NotRegisteredBeansProblem.class); - private static final List AOT_BEANS = List.of( "org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor", "org.springframework.beans.factory.aot.BeanRegistrationAotProcessor", "org.springframework.beans.factory.aot.RuntimeHintsRegistrar" ); - @Override - public String getRecipeId() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getLabel(RecipeScope s) { - // TODO Auto-generated method stub - return null; - } - - @Override - public RecipeScope[] getScopes() { - // TODO Auto-generated method stub - return new RecipeScope[0]; - } - @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @@ -73,7 +60,6 @@ public class NotRegisteredBeansProblem implements RecipeSpringJavaProblemDescrip for (SymbolAddOnInformation info : additionalInformation) { if (info instanceof BeansSymbolAddOnInformation) { BeansSymbolAddOnInformation info2 = (BeansSymbolAddOnInformation) info; -// log.info("Bean: id=" + info2.getBeanID() + ", type=" + info2.getBeanType()); return beanClassName.equals(info2.getBeanType()); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/PreciseBeanTypeProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/PreciseBeanTypeProblem.java index f0b936e16..861626ce4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/PreciseBeanTypeProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/PreciseBeanTypeProblem.java @@ -12,16 +12,19 @@ package org.springframework.ide.vscode.boot.java.rewrite.reconcile; import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; +import java.util.List; + import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.Return; -import org.openrewrite.marker.Range; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.marker.Range; import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -29,28 +32,16 @@ import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDes import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor { + private static final String RECIPE_ID = "org.openrewrite.java.spring.boot3.PreciseBeanType"; + private static final String LABEL = "Ensure concrete bean type"; private static final String MSG_KEY = "returnType"; - @Override - public String getRecipeId() { - return "org.openrewrite.java.spring.boot3.PreciseBeanType"; - } - - @Override - public String getLabel(RecipeScope s) { - return RecipeCodeActionDescriptor.buildLabel(LABEL, s); - } - - @Override - public RecipeScope[] getScopes() { - return RecipeScope.values(); - } - @Override public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor<>() { @@ -62,8 +53,19 @@ public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor if (o != null && !o.equals(m.getReturnTypeExpression().getType())) { if ((o instanceof JavaType.FullyQualified && m.getReturnTypeExpression().getType() instanceof JavaType.FullyQualified) || (o instanceof JavaType.Array && m.getReturnTypeExpression().getType() instanceof JavaType.Array)) { - m = m.withReturnTypeExpression(m.getReturnTypeExpression().withMarkers(m.getReturnTypeExpression().getMarkers().add( - new FixAssistMarker(Tree.randomId(), getId()).withScope(m.getMarkers().findFirst(Range.class).get()).withRecipeId(getRecipeId())))); + + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); + FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()) + .withFixes( + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.NODE)) + .withRecipeScope(RecipeScope.NODE) + .withRangeScope(m.getMarkers().findFirst(Range.class).get()), + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE)) + .withRecipeScope(RecipeScope.FILE), + new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT) + ); + m = m.withReturnTypeExpression(m.getReturnTypeExpression().withMarkers(m.getReturnTypeExpression().getMarkers().add(marker))); } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/UnnecessarySpringExtensionProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/UnnecessarySpringExtensionProblem.java index 8dbab5d74..652a131b2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/UnnecessarySpringExtensionProblem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/UnnecessarySpringExtensionProblem.java @@ -10,17 +10,84 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.rewrite.reconcile; +import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; + +import java.util.Arrays; +import java.util.List; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; +import org.openrewrite.Tree; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.AnnotationMatcher; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.tree.J.ClassDeclaration; +import org.openrewrite.java.tree.JavaType.FullyQualified; +import org.openrewrite.java.tree.TypeUtils; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; -import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor; +import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; -public class UnnecessarySpringExtensionProblem extends UnnecessarySpringExtensionCodeAction - implements RecipeSpringJavaProblemDescriptor { +public class UnnecessarySpringExtensionProblem implements RecipeSpringJavaProblemDescriptor { + + private static final String LABEL = "Remove unnecessary @SpringExtension"; + private static final String ID = "org.openrewrite.java.spring.boot2.UnnecessarySpringExtension"; + private static final List SPRING_BOOT_TEST_ANNOTATIONS = Arrays.asList( + "org.springframework.boot.test.context.SpringBootTest", + "org.springframework.boot.test.autoconfigure.jdbc.JdbcTest", + "org.springframework.boot.test.autoconfigure.web.client.RestClientTest", + "org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest", + "org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest", + "org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest", + "org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest", + "org.springframework.boot.test.autoconfigure.jooq.JooqTest", + "org.springframework.boot.test.autoconfigure.json.JsonTest", + "org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest", + "org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest", + "org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest", + "org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest", + "org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest", + "org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest", + "org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest" + ); + private static final AnnotationMatcher SPRING_EXTENSION_ANNOTATIN_MATCHER = new AnnotationMatcher("@org.junit.jupiter.api.extension.ExtendWith(org.springframework.test.context.junit.jupiter.SpringExtension.class)"); @Override - public RecipeScope[] getScopes() { - return new RecipeScope[] { RecipeScope.FILE }; + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { + return new JavaIsoVisitor<>() { + + @Override + public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) { + ClassDeclaration c = super.visitClassDeclaration(classDecl, p); + if (c.getLeadingAnnotations().stream().anyMatch(a -> { + FullyQualified fq = TypeUtils.asFullyQualified(a.getType()); + return fq != null && SPRING_BOOT_TEST_ANNOTATIONS.contains(fq.getFullyQualifiedName()); + })) { + c = c.withLeadingAnnotations(ListUtils.map(c.getLeadingAnnotations(), a -> { + if (SPRING_EXTENSION_ANNOTATIN_MATCHER.matches(a)) { + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri().toString(); + FixAssistMarker fixMarker = new FixAssistMarker(Tree.randomId(), getId()) + .withFix(new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT))); + return a.withMarkers(a.getMarkers().add(fixMarker)); + } + return a; + })); + } + return c; + } + + }; + } + + @Override + public boolean isApplicable(IJavaProject project) { + return springBootVersionGreaterOrEqual(2, 1, 0).test(project); } @Override diff --git a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json index ce90e0a34..b065a99a6 100644 --- a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json +++ b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json @@ -56,10 +56,16 @@ "defaultSeverity": "WARNING" }, { - "code": "JAVA_BEAN_NOT_REGISTERED_IN_AOT", + "code": "JAVA_BEAN_POST_PROCESSOR_IGNORED_IN_AOT", "label": "'BeanPostProcessor' behaviour is ignored in AOT", "description": "'BeanPostProcessor' behaviour is ignored in Spring 6 AOT", "defaultSeverity": "WARNING" + }, + { + "code": "JAVA_BEAN_NOT_REGISTERED_IN_AOT", + "label": "Not registered as a Bean", + "description": "Not registered as Bean", + "defaultSeverity": "WARNING" } ] }, diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 162014aad..221702b69 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -336,7 +336,7 @@ "ERROR" ] }, - "spring-boot.ls.problem.boot3.JAVA_BEAN_NOT_REGISTERED_IN_AOT": { + "spring-boot.ls.problem.boot3.JAVA_BEAN_POST_PROCESSOR_IGNORED_IN_AOT": { "type": "string", "default": "WARNING", "description": "'BeanPostProcessor' behaviour is ignored in Spring 6 AOT", @@ -347,6 +347,18 @@ "HINT", "ERROR" ] + }, + "spring-boot.ls.problem.boot3.JAVA_BEAN_NOT_REGISTERED_IN_AOT": { + "type": "string", + "default": "WARNING", + "description": "Not registered as Bean", + "enum": [ + "IGNORE", + "INFO", + "WARNING", + "HINT", + "ERROR" + ] } } },