Refactorings. Bean methods not public.

This commit is contained in:
BoykoAlex
2022-05-06 18:49:07 -04:00
parent 49873660e6
commit 8102c19f8a
23 changed files with 493 additions and 109 deletions

View File

@@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory;
public class SpringProjectUtil {
public static final String SPRING_BOOT = "spring-boot";
private static final String VERION_PATTERN_STR = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?";
public static final Logger log = LoggerFactory.getLogger(SpringProjectUtil.class);
@@ -37,7 +39,7 @@ public class SpringProjectUtil {
}
public static boolean isBootProject(IJavaProject jp) {
return hasSpecificLibraryOnClasspath(jp, "spring-boot", true);
return hasSpecificLibraryOnClasspath(jp, SPRING_BOOT, true);
}
public static boolean hasBootActuators(IJavaProject jp) {

View File

@@ -19,6 +19,7 @@ import java.util.Set;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
@@ -105,16 +106,16 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
this.codeActionHandler = new CodeActionHandler() {
@Override
public List<Either<Command, CodeAction>> handle(CancelChecker cancelToken, CodeActionCapabilities capabilities, TextDocument doc,
IRegion region) {
public List<Either<Command, CodeAction>> handle(CancelChecker cancelToken,
CodeActionCapabilities capabilities, CodeActionContext context, TextDocument doc, IRegion region) {
LanguageId language = doc.getLanguageId();
LanguageServerComponents subComponents = componentsByLanguageId.get(language);
if (subComponents != null) {
return subComponents.getCodeActionProvider()
.map(subEngine -> subEngine.handle(cancelToken, capabilities, doc, region))
.map(subEngine -> subEngine.handle(cancelToken, capabilities, context, doc, region))
.orElse(Collections.emptyList());
}
//No applicable subEngine...
// No applicable subEngine...
return Collections.emptyList();
}
};

View File

@@ -1,9 +1,20 @@
/*******************************************************************************
* 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.languageserver.util;
import java.util.List;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -12,6 +23,6 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
public interface CodeActionHandler {
List<Either<Command, CodeAction>> handle(CancelChecker cancelToken, CodeActionCapabilities capabilities, TextDocument doc, IRegion region);
List<Either<Command, CodeAction>> handle(CancelChecker cancelToken, CodeActionCapabilities capabilities, CodeActionContext context, TextDocument doc, IRegion region);
}

View File

@@ -27,6 +27,8 @@ import java.util.stream.Collectors;
import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CodeLensParams;
@@ -82,6 +84,7 @@ import org.springframework.ide.vscode.commons.util.text.Region;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
public class SimpleTextDocumentService implements TextDocumentService, DocumentEventListenerManager {
@@ -425,23 +428,27 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
}
private List<Either<Command, CodeAction>> computeCodeActions(CancelChecker cancelToken, CodeActionCapabilities capabilities, TrackedDocument doc, CodeActionParams params) {
List<Either<Command,CodeAction>> list = doc.getQuickfixes().stream()
.filter((fix) -> fix.appliesTo(params.getRange(), params.getContext()))
.map(f -> f.getCodeAction(params.getContext()))
.map(command -> Either.<Command, CodeAction>forRight(command))
.collect(Collectors.toList());
Builder<Either<Command,CodeAction>> listBuilder = ImmutableList.builder();
CodeActionContext context = params.getContext();
if (!context.getDiagnostics().isEmpty() || (context.getOnly() != null && context.getOnly().contains(CodeActionKind.QuickFix))) {
doc.getQuickfixes().stream()
.filter((fix) -> fix.appliesTo(params.getRange(), context))
.map(f -> f.getCodeAction(params.getContext()))
.map(command -> Either.<Command, CodeAction>forRight(command))
.forEach(listBuilder::add);
}
if (codeActionHandler != null) {
try {
int start = doc.getDocument().toOffset(params.getRange().getStart());
int end = doc.getDocument().toOffset(params.getRange().getEnd());
list.addAll(codeActionHandler.handle(cancelToken, capabilities, doc.getDocument(), new Region(start, end - start)));
listBuilder.addAll(codeActionHandler.handle(cancelToken, capabilities, context, doc.getDocument(), new Region(start, end - start)));
} catch (Exception e) {
log.error("Failed to compute quick refactorings", e);
}
}
return list;
return listBuilder.build();
}
private static CodeActionCapabilities getCodeActionCapabilities(ClientCapabilities capabilities) {

View File

@@ -15,14 +15,17 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.boot.java.handlers.AutowiredConstructorReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.AutowiredConstructorReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.BeanMethodNotPublicReconciler;
import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNoPublicCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.ConvertAutowiredField;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.NoRequestMapping;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.NoRequestMappings;
import org.springframework.ide.vscode.boot.java.rewrite.quickfix.AutowiredConstructorQuickFixHandler;
import org.springframework.ide.vscode.boot.java.rewrite.quickfix.BeanMethodNoPublicQuickFixHandler;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@@ -62,11 +65,20 @@ public class RewriteConfig implements InitializingBean {
return new NoRequestMappings(server, projectFinder, rewriteRefactorings, orCuCache);
}
@ConditionalOnClass({org.openrewrite.java.spring.BeanMethodsNotPublic.class})
@Bean
BeanMethodsNoPublicCodeAction beanMethodsNoPublic(SimpleLanguageServer server, JavaProjectFinder projectFinder,
RewriteRefactorings rewriteRefactorings, RewriteRecipeRepository recipesRepo,
ORCompilationUnitCache orCuCache) {
return new BeanMethodsNoPublicCodeAction(server, projectFinder, rewriteRefactorings, orCuCache);
}
@Override
public void afterPropertiesSet() throws Exception {
QuickfixRegistry registry = server.getQuickfixRegistry();
registry.register(AutowiredConstructorReconciler.REMOVE_UNNECESSARY_AUTOWIRED_FROM_CONSTRUCTOR, new AutowiredConstructorQuickFixHandler(server, projectFinder, orCuCache));
registry.register(BeanMethodNotPublicReconciler.REMOVE_PUBLIC_FROM_BEAN_METHOD, new BeanMethodNoPublicQuickFixHandler(server, projectFinder, orCuCache));
}

View File

@@ -24,8 +24,10 @@ public enum SpringJavaProblemType implements ProblemType {
JAVA_SPEL_EXPRESSION_SYNTAX(ERROR, "SpEL parser raised a ParseException", "SpEL Expression Syntax"),
JAVA_AUTOWIRED_CONSTRUCTOR(WARNING, "Unnecessary @Autowired over the only constructor", "Unnecessary @Autowired");
JAVA_AUTOWIRED_CONSTRUCTOR(WARNING, "Unnecessary @Autowired over the only constructor", "Unnecessary @Autowired"),
JAVA_PUBLIC_BEAN_METHOD(HINT, "Public modifier on @Bean method. They no longer have to be public visibility to be usable by Spring.", "public @Bean method");
private final ProblemSeverity defaultSeverity;
private String description;
private String label;

View File

@@ -21,6 +21,7 @@ import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -44,14 +45,14 @@ public class BootJavaCodeActionProvider implements CodeActionHandler {
}
@Override
public List<Either<Command, CodeAction>> handle(CancelChecker cancelToken, CodeActionCapabilities capabilities, TextDocument doc, IRegion region) {
public List<Either<Command, CodeAction>> handle(CancelChecker cancelToken, CodeActionCapabilities capabilities, CodeActionContext context, TextDocument doc, IRegion region) {
Optional<IJavaProject> project = projectFinder.find(doc.getId());
if (project.isPresent()) {
return cuCache.withCompilationUnit(project.get(), URI.create(doc.getId().getUri()), cu -> {
ASTNode found = NodeFinder.perform(cu, region.getOffset(), region.getLength());
List<Either<Command, CodeAction>> codeActions = new ArrayList<>();
for (JavaCodeAction jca : javaCodeActions) {
List<Either<Command, CodeAction>> cas = jca.getCodeActions(capabilities, doc, region, project.get(), cu, found);
List<Either<Command, CodeAction>> cas = jca.getCodeActions(capabilities, context, doc, region, project.get(), cu, found);
if (cas != null) {
codeActions.addAll(cas);
}

View File

@@ -20,6 +20,10 @@ import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationParamReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.AutowiredConstructorReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.BeanMethodNotPublicReconciler;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.value.Constants;
import org.springframework.ide.vscode.commons.java.IJavaProject;
@@ -87,7 +91,8 @@ public class BootJavaReconcileEngine implements IReconcileEngine {
new AnnotationParamReconciler(SPRING_CONDITIONAL_ON_EXPRESSION, null, "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CONDITIONAL_ON_EXPRESSION, "value", "", "", spelExpressionReconciler),
new AutowiredConstructorReconciler(quickfixRegistry)
new AutowiredConstructorReconciler(quickfixRegistry),
new BeanMethodNotPublicReconciler(quickfixRegistry)
};
}

View File

@@ -16,6 +16,7 @@ import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -27,6 +28,6 @@ public interface JavaCodeAction {
default WorkspaceEdit perform(List<?> args) { return null; }
List<Either<Command, CodeAction>> getCodeActions(CodeActionCapabilities capabilities, TextDocument doc, IRegion region, IJavaProject project, CompilationUnit cu, ASTNode node);
List<Either<Command, CodeAction>> getCodeActions(CodeActionCapabilities capabilities, CodeActionContext context, TextDocument doc, IRegion region, IJavaProject project, CompilationUnit cu, ASTNode node);
}

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.handlers;
package org.springframework.ide.vscode.boot.java.reconcilers;
import java.util.List;
import java.util.Set;
@@ -21,6 +21,7 @@ import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.util.text.IDocument;

View File

@@ -8,7 +8,7 @@
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.handlers;
package org.springframework.ide.vscode.boot.java.reconcilers;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;

View File

@@ -8,7 +8,7 @@
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.handlers;
package org.springframework.ide.vscode.boot.java.reconcilers;
import java.util.List;
import java.util.Optional;
@@ -41,7 +41,7 @@ public class AutowiredConstructorReconciler implements AnnotationReconciler {
@Override
public void visit(IJavaProject project, IDocument doc, Annotation node, ITypeBinding typeBinding, IProblemCollector problemCollector) {
Version version = SpringProjectUtil.getDependencyVersion(project, "spring-boot");
Version version = SpringProjectUtil.getDependencyVersion(project, SpringProjectUtil.SPRING_BOOT);
if (version.getMajor() >= 2) {
getSingleAutowiredConstructorDeclaringType(node, typeBinding).ifPresent(type -> {

View File

@@ -0,0 +1,113 @@
/*******************************************************************************
* 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.reconcilers;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.commons.util.text.IDocument;
public class BeanMethodNotPublicReconciler implements AnnotationReconciler {
private static final Logger log = LoggerFactory.getLogger(BeanMethodNotPublicReconciler.class);
public static final String REMOVE_PUBLIC_FROM_BEAN_METHOD = "RemovePublicFromBeanMethod";
private QuickfixRegistry quickfixRegistry;
public BeanMethodNotPublicReconciler(QuickfixRegistry quickfixRegistry) {
this.quickfixRegistry = quickfixRegistry;
}
@Override
public void visit(IJavaProject project, IDocument doc, Annotation node, ITypeBinding typeBinding,
IProblemCollector problemCollector) {
if (Annotations.BEAN.equals(typeBinding.getQualifiedName()) && node.getParent() instanceof MethodDeclaration) {
MethodDeclaration m = (MethodDeclaration) node.getParent();
Version version = SpringProjectUtil.getDependencyVersion(project, SpringProjectUtil.SPRING_BOOT);
if (version.getMajor() >= 2) {
IMethodBinding methodBinding = m.resolveBinding();
if (isNotOverridingPublicMethod(methodBinding)) {
ReconcileProblemImpl problem = ((List<?>)m.modifiers()).stream()
.filter(Modifier.class::isInstance)
.map(Modifier.class::cast)
.filter(modifier -> modifier.isPublic())
.findFirst()
.map(modifier -> new ReconcileProblemImpl(
SpringJavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "public @Bean method",
modifier.getStartPosition(), modifier.getLength()))
.orElse(new ReconcileProblemImpl(
SpringJavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "public @Bean method",
m.getName().getStartPosition(), m.getName().getLength()));
QuickfixType quickfixType = quickfixRegistry.getQuickfixType(REMOVE_PUBLIC_FROM_BEAN_METHOD);
if (quickfixType != null) {
problem.addQuickfix(new QuickfixData<>(
quickfixType,
createParameters(doc, methodBinding),
"Remove public from @Bean method"
));
}
problemCollector.accept(problem);
}
}
}
}
private static final boolean isOverriding(IMethodBinding binding) {
try {
Field f = binding.getClass().getDeclaredField("binding");
f.setAccessible(true);
org.eclipse.jdt.internal.compiler.lookup.MethodBinding value = (org.eclipse.jdt.internal.compiler.lookup.MethodBinding) f.get(binding);
return value.isOverriding();
} catch (Exception e) {
log.error("", e);
}
return false;
}
public static final boolean isNotOverridingPublicMethod(IMethodBinding methodBinding) {
return !isOverriding(methodBinding) && (methodBinding.getModifiers() & Modifier.PUBLIC) != 0;
}
public static final List<String> createParameters(IDocument doc, IMethodBinding methodBinding) {
StringBuilder methodPattern = new StringBuilder(methodBinding.getDeclaringClass().getQualifiedName());
methodPattern.append(' ');
methodPattern.append(methodBinding.getName());
methodPattern.append('(');
methodPattern.append(Arrays.stream(methodBinding.getParameterTypes()).map(p -> p.getErasure().getQualifiedName()).collect(Collectors.joining(",")));
methodPattern.append(')');
return List.of(doc.getUri(), methodPattern.toString());
}
}

View File

@@ -292,6 +292,10 @@ public class ORAstUtils {
}
}
public static Recipe nodeRecipe(JavaVisitor<ExecutionContext> v, Predicate<J> condition) {
return new NodeRecipe((JavaVisitor<ExecutionContext>) v, condition);
}
@SuppressWarnings("unchecked")
public static Recipe nodeRecipe(Recipe r, Predicate<J> condition) {
return new NodeRecipe((JavaVisitor<ExecutionContext>) getVisitor(r), condition);

View File

@@ -12,7 +12,9 @@ package org.springframework.ide.vscode.boot.java.rewrite;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
@@ -264,4 +266,17 @@ public class ORCompilationUnitCache implements DocumentContentProvider, Disposab
return requestor.apply(null);
}
public List<CompilationUnit> getCompiulationUnits(IJavaProject project) {
List<Path> javaFiles = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> {
try {
return Files.walk(folder.toPath());
} catch (IOException e) {
logger.error("", e);
}
return Stream.empty();
}).filter(Files::isRegularFile).filter(p -> p.getFileName().toString().endsWith(".java")).collect(Collectors.toList());
JavaParser javaParser = loadJavaParser(project);
return ORAstUtils.parse(javaParser, javaFiles);
}
}

View File

@@ -10,24 +10,39 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.codeaction;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionResolveSupportCapabilities;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.ResourceOperation;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.Recipe;
import org.openrewrite.Result;
import org.openrewrite.java.tree.J;
import org.springframework.ide.vscode.boot.java.handlers.JavaCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache;
import org.springframework.ide.vscode.boot.java.rewrite.ORDocUtils;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
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.LspClient;
import org.springframework.ide.vscode.commons.languageserver.util.LspClient.Client;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.text.IRegion;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
public abstract class AbstractRewriteJavaCodeAction implements JavaCodeAction {
@@ -54,17 +69,22 @@ public abstract class AbstractRewriteJavaCodeAction implements JavaCodeAction {
return ca;
}
protected WorkspaceEdit applyRecipe(Recipe r, TextDocument doc, org.openrewrite.java.tree.J.CompilationUnit cu) {
List<Result> results = r.run(List.of(cu));
if (!results.isEmpty() && results.get(0).getAfter() != null) {
Optional<TextDocumentEdit> edit = ORDocUtils.computeTextDocEdit(doc, results.get(0));
return edit.map(e -> {
WorkspaceEdit workspaceEdit = new WorkspaceEdit();
workspaceEdit.setDocumentChanges(List.of(Either.forLeft(e)));
return workspaceEdit;
}).orElse(null);
protected WorkspaceEdit applyRecipe(Recipe r, IJavaProject project, List<J.CompilationUnit> cus) {
List<Result> results = r.run(cus);
List<Either<TextDocumentEdit, ResourceOperation>> edits = results.stream().filter(res -> res.getAfter() != null).map(res -> {
URI docUri = project.getLocationUri().resolve(res.getAfter().getSourcePath().toString());
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docUri.toString());
if (doc == null) {
doc = new TextDocument(docUri.toString(), LanguageId.JAVA, 0, res.getBefore() == null ? "" : res.getBefore().printAll());
}
return ORDocUtils.computeTextDocEdit(doc, res);
}).filter(e -> e.isPresent()).map(e -> e.get()).map(e -> Either.<TextDocumentEdit, ResourceOperation>forLeft(e)).collect(Collectors.toList());
if (edits.isEmpty()) {
return null;
}
return null;
WorkspaceEdit workspaceEdit = new WorkspaceEdit();
workspaceEdit.setDocumentChanges(edits);
return workspaceEdit;
}
protected static boolean isResolve(CodeActionCapabilities capabilities, String property) {
@@ -77,5 +97,58 @@ public abstract class AbstractRewriteJavaCodeAction implements JavaCodeAction {
}
return false;
}
protected boolean isSupported(CodeActionCapabilities capabilities, CodeActionContext context) {
// Default case is anything non-quick fix related.
if (isResolve(capabilities, "edit")) {
if (context.getOnly() != null) {
return context.getOnly().contains(CodeActionKind.Refactor);
} else {
if (LspClient.currentClient() == Client.ECLIPSE) {
// Eclipse would have no diagnostics in the context for QuickAssists refactoring. Diagnostics will be around for QuickFix only
return context.getDiagnostics().isEmpty();
} else {
return true;
}
}
}
return false;
}
@Override
final public List<Either<Command, CodeAction>> getCodeActions(CodeActionCapabilities capabilities,
CodeActionContext context, TextDocument doc, IRegion region, IJavaProject project, CompilationUnit cu,
ASTNode node) {
if (isSupported(capabilities, context)) {
return provideCodeActions(context, doc, region, project, cu, node);
}
return null;
}
final protected WorkspaceEdit perform(List<?> args, Supplier<Recipe> recipeSupplier) {
String docUri = (String) args.get(0);
boolean projectWide = ((Boolean) args.get(1)).booleanValue();
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(docUri));
if (project.isPresent()) {
if (projectWide) {
return applyRecipe(recipeSupplier.get(), project.get(), orCuCache.getCompiulationUnits(project.get()));
} else {
return orCuCache.withCompilationUnit(project.get(), URI.create(docUri), cu -> {
if (cu == null) {
throw new IllegalStateException("Cannot parse Java file: " + docUri);
}
return applyRecipe(recipeSupplier.get(), project.get(), List.of(cu));
});
}
}
return null;
}
protected abstract List<Either<Command, CodeAction>> provideCodeActions(CodeActionContext context, TextDocument doc,
IRegion region, IJavaProject project, CompilationUnit cu, ASTNode node);
}

View File

@@ -0,0 +1,83 @@
/*******************************************************************************
* 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 java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.java.spring.BeanMethodsNotPublic;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.reconcilers.BeanMethodNotPublicReconciler;
import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
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.IRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class BeanMethodsNoPublicCodeAction extends AbstractRewriteJavaCodeAction {
private static final String REMOVE_PUBLIC_FROM_BEAN_METHODS_IN_FILE = "Remove 'public' from @Bean methods in file";
private static final String REMOVE_PUBLIC_FROM_BEAN_METHODS_IN_PROJECT = "Remove 'public' from @Bean methods in project";
private static final String CODE_ACTION_ID = "RemovePublicFromBeanMethod";
public BeanMethodsNoPublicCodeAction(SimpleLanguageServer server, JavaProjectFinder projectFinder,
RewriteRefactorings rewriteRefactorings, ORCompilationUnitCache orCuCache) {
super(server, projectFinder, rewriteRefactorings, orCuCache, CODE_ACTION_ID);
}
@Override
protected List<Either<Command, CodeAction>> provideCodeActions(CodeActionContext context, TextDocument doc,
IRegion region, IJavaProject project, CompilationUnit cu, ASTNode node) {
for (; node != null && !(node instanceof MethodDeclaration); node = node.getParent()) {
// nothing
}
if (node instanceof MethodDeclaration) {
MethodDeclaration m = (MethodDeclaration) node;
IMethodBinding binding = m.resolveBinding();
if (binding != null) {
Optional<IAnnotationBinding> beanAnnotationPresent = Arrays.stream(binding.getAnnotations())
.filter(a -> Annotations.BEAN.equals(a.getAnnotationType().getQualifiedName()))
.findFirst();
if (beanAnnotationPresent.isPresent() && BeanMethodNotPublicReconciler.isNotOverridingPublicMethod(binding)) {
Version version = SpringProjectUtil.getDependencyVersion(project, SpringProjectUtil.SPRING_BOOT);
if (version.getMajor() >= 2) {
return List.of(
Either.forRight(createCodeAction(REMOVE_PUBLIC_FROM_BEAN_METHODS_IN_FILE, List.of(doc.getUri(), false))),
Either.forRight(createCodeAction(REMOVE_PUBLIC_FROM_BEAN_METHODS_IN_PROJECT, List.of(doc.getUri(), true)))
);
}
}
}
}
return null;
}
@Override
public WorkspaceEdit perform(List<?> args) {
return perform(args, () -> new BeanMethodsNotPublic());
}
}

View File

@@ -23,7 +23,7 @@ import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
@@ -35,7 +35,6 @@ import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
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.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.IRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -50,11 +49,9 @@ public class ConvertAutowiredField extends AbstractRewriteJavaCodeAction {
@Override
public WorkspaceEdit perform(List<?> args) {
SimpleTextDocumentService documents = server.getTextDocumentService();
String docUri = (String) args.get(0);
String classFqName = (String) args.get(1);
String fieldName = (String) args.get(2);
TextDocument doc = documents.getLatestSnapshot(docUri);
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(docUri));
@@ -63,20 +60,15 @@ public class ConvertAutowiredField extends AbstractRewriteJavaCodeAction {
if (cu == null) {
throw new IllegalStateException("Cannot parse Java file: " + docUri);
}
return applyRecipe(new ConvertAutowiredParameterIntoConstructorParameter(classFqName, fieldName), doc, cu);
return applyRecipe(new ConvertAutowiredParameterIntoConstructorParameter(classFqName, fieldName), project.get(), List.of(cu));
});
}
return null;
}
@Override
public List<Either<Command, CodeAction>> getCodeActions(CodeActionCapabilities capabilities, TextDocument doc, IRegion region, IJavaProject project,
CompilationUnit cu, ASTNode node) {
// Only supports resolvable code action for now
if (!isResolve(capabilities, "edit")) {
return Collections.emptyList();
}
protected List<Either<Command, CodeAction>> provideCodeActions(CodeActionContext context, TextDocument doc, IRegion region, IJavaProject project,
CompilationUnit cu, ASTNode node) {
for (; node != null && !(node instanceof FieldDeclaration); node = node.getParent()) {
// nothing
}

View File

@@ -21,7 +21,7 @@ import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
@@ -34,7 +34,6 @@ import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
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.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.IRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -49,10 +48,8 @@ public class NoRequestMapping extends NoRequestMappings {
@Override
public WorkspaceEdit perform(List<?> args) {
SimpleTextDocumentService documents = server.getTextDocumentService();
String docUri = (String) args.get(0);
String matchStr = (String) args.get(1);
TextDocument doc = documents.getLatestSnapshot(docUri);
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(docUri));
@@ -69,19 +66,15 @@ public class NoRequestMapping extends NoRequestMappings {
return macther.matches(m.getMethodType());
}
return false;
}), doc, cu);
}), project.get(), List.of(cu));
});
}
return null;
}
@Override
public List<Either<Command, CodeAction>> getCodeActions(CodeActionCapabilities capabilities, TextDocument doc, IRegion region, IJavaProject project,
protected List<Either<Command, CodeAction>> provideCodeActions(CodeActionContext context, TextDocument doc, IRegion region, IJavaProject project,
CompilationUnit cu, ASTNode node) {
// Only supports resolvable code action for now
if (!isResolve(capabilities, "edit")) {
return Collections.emptyList();
}
return findAppropriateMethodDeclaration(node).map(method -> {
String methodMatcher = "* " + method.getName().getIdentifier() + "(*)";
IMethodBinding methodBinding = method.resolveBinding();

View File

@@ -10,7 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.codeaction;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -21,9 +20,8 @@ import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.java.spring.NoRequestMappingAnnotation;
@@ -33,7 +31,6 @@ import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
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.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.IRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -70,33 +67,16 @@ public class NoRequestMappings extends AbstractRewriteJavaCodeAction {
@Override
public WorkspaceEdit perform(List<?> args) {
SimpleTextDocumentService documents = server.getTextDocumentService();
String docUri = (String) args.get(0);
TextDocument doc = documents.getLatestSnapshot(docUri);
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(docUri));
if (project.isPresent()) {
return orCuCache.withCompilationUnit(project.get(), URI.create(docUri), cu -> {
if (cu == null) {
throw new IllegalStateException("Cannot parse Java file: " + docUri);
}
return applyRecipe(new NoRequestMappingAnnotation(), doc, cu);
});
}
return null;
return perform(args, () -> new NoRequestMappingAnnotation());
}
@Override
public List<Either<Command, CodeAction>> getCodeActions(CodeActionCapabilities capabilities, TextDocument doc, IRegion region, IJavaProject project,
protected List<Either<Command, CodeAction>> provideCodeActions(CodeActionContext context, TextDocument doc, IRegion region, IJavaProject project,
CompilationUnit cu, ASTNode node) {
// Only supports resolvable code action for now
if (!isResolve(capabilities, "edit")) {
return Collections.emptyList();
}
return findAppropriateMethodDeclaration(node).map(m -> createCodeAction("Replace all @RequestMapping with @GetMapping etc.", List.of(
doc.getId().getUri()
))).map(ca -> List.of(Either.<Command, CodeAction>forRight(ca))).orElse(Collections.emptyList());
return findAppropriateMethodDeclaration(node).map(m -> List.of(
Either.<Command, CodeAction>forRight(createCodeAction("Replace @RequestMapping with @GetMapping etc. in file", List.of(doc.getId().getUri(), false))),
Either.<Command, CodeAction>forRight(createCodeAction("Replace @RequestMapping with @GetMapping etc. in project", List.of(doc.getId().getUri(), true)))
)).orElse(Collections.emptyList());
}

View File

@@ -10,19 +10,13 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.quickfix;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.Result;
import org.openrewrite.java.spring.NoAutowiredOnConstructor;
import org.springframework.ide.vscode.boot.java.rewrite.ORAstUtils;
import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache;
import org.springframework.ide.vscode.boot.java.rewrite.ORDocUtils;
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;
@@ -58,28 +52,12 @@ public class AutowiredConstructorQuickFixHandler extends RewriteQuickFixHandler
}
private WorkspaceEdit removeUnnecessaryAutowiredFromConstructor(IJavaProject project, TextDocument doc, String classFqName) {
String docUri = doc.getId().getUri();
return orCuCache.withCompilationUnit(project, URI.create(docUri), cu -> {
if (cu == null) {
throw new IllegalStateException("Cannot parse Java file: " + docUri);
return applyRecipe(orCuCache, new NoAutowiredOnConstructor(), project, doc, t -> {
if (t instanceof org.openrewrite.java.tree.J.ClassDeclaration) {
org.openrewrite.java.tree.J.ClassDeclaration c = (org.openrewrite.java.tree.J.ClassDeclaration) t;
return c.getType() != null && classFqName.equals(c.getType().getFullyQualifiedName());
}
List<Result> results = ORAstUtils.nodeRecipe(new NoAutowiredOnConstructor(), t -> {
if (t instanceof org.openrewrite.java.tree.J.ClassDeclaration) {
org.openrewrite.java.tree.J.ClassDeclaration c = (org.openrewrite.java.tree.J.ClassDeclaration) t;
return c.getType() != null && classFqName.equals(c.getType().getFullyQualifiedName());
}
return false;
}).run(List.of(cu));
if (!results.isEmpty() && results.get(0).getAfter() != null) {
Optional<TextDocumentEdit> edit = ORDocUtils.computeTextDocEdit(doc, results.get(0));
return edit.map(e -> {
WorkspaceEdit workspaceEdit = new WorkspaceEdit();
workspaceEdit.setDocumentChanges(List.of(Either.forLeft(e)));
return workspaceEdit;
}).orElse(null);
}
return null;
return false;
});
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* 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.quickfix;
import java.util.List;
import java.util.Optional;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.spring.BeanMethodsNotPublic;
import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache;
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.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class BeanMethodNoPublicQuickFixHandler extends RewriteQuickFixHandler {
final private SimpleLanguageServer server;
final private JavaProjectFinder projectFinder;
final private ORCompilationUnitCache orCuCache;
public BeanMethodNoPublicQuickFixHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder,
ORCompilationUnitCache orCuCache) {
this.server = server;
this.projectFinder = projectFinder;
this.orCuCache = orCuCache;
}
@Override
protected WorkspaceEdit perform(List<?> args) {
SimpleTextDocumentService documents = server.getTextDocumentService();
String docUri = (String) args.get(0);
String methodMatch = (String) args.get(1);
TextDocument doc = documents.getLatestSnapshot(docUri);
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(docUri));
if (project.isPresent()) {
MethodMatcher matcher = new MethodMatcher(methodMatch);
return applyRecipe(orCuCache, new BeanMethodsNotPublic(), project.get(), doc, t -> {
if (t instanceof org.openrewrite.java.tree.J.MethodDeclaration) {
org.openrewrite.java.tree.J.MethodDeclaration m = (org.openrewrite.java.tree.J.MethodDeclaration) t;
return matcher.matches(m.getMethodType());
}
return false;
});
}
return null;
}
}

View File

@@ -10,11 +10,26 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.quickfix;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.Result;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.springframework.ide.vscode.boot.java.rewrite.ORAstUtils;
import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache;
import org.springframework.ide.vscode.boot.java.rewrite.ORDocUtils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixHandler;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
@@ -34,4 +49,33 @@ public abstract class RewriteQuickFixHandler implements QuickfixHandler {
abstract protected WorkspaceEdit perform(List<?> l);
final protected WorkspaceEdit applyRecipe(ORCompilationUnitCache orCuCache, Recipe r, IJavaProject project, TextDocument doc, Predicate<J> nodeFilter) {
return doApplyRecipe(orCuCache, ORAstUtils.nodeRecipe(r, nodeFilter), project, doc);
}
final protected WorkspaceEdit applyVisitor(ORCompilationUnitCache orCuCache, JavaVisitor<ExecutionContext> v, IJavaProject project, TextDocument doc, Predicate<J> nodeFilter) {
return doApplyRecipe(orCuCache, ORAstUtils.nodeRecipe(v, nodeFilter), project, doc);
}
final protected WorkspaceEdit doApplyRecipe(ORCompilationUnitCache orCuCache, Recipe r, IJavaProject project, TextDocument doc) {
String docUri = doc.getId().getUri();
return orCuCache.withCompilationUnit(project, URI.create(docUri), cu -> {
if (cu == null) {
throw new IllegalStateException("Cannot parse Java file: " + docUri);
}
List<Result> results = r.run(List.of(cu));
if (!results.isEmpty() && results.get(0).getAfter() != null) {
Optional<TextDocumentEdit> edit = ORDocUtils.computeTextDocEdit(doc, results.get(0));
return edit.map(e -> {
WorkspaceEdit workspaceEdit = new WorkspaceEdit();
workspaceEdit.setDocumentChanges(List.of(Either.forLeft(e)));
return workspaceEdit;
}).orElse(null);
}
return null;
});
}
}