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 4df00128c..bc992f09b 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 @@ -16,6 +16,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J.MethodDeclaration; import org.openrewrite.marker.Range; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; @@ -44,14 +45,14 @@ public class HelloMethodRenameProblemDescriptor implements RecipeSpringJavaProbl } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor<>() { @Override public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, ExecutionContext p) { MethodDeclaration m = super.visitMethodDeclaration(method, p); if ("hello".equals(method.getSimpleName())) { - FixAssistMarker marker = new FixAssistMarker(Tree.randomId()).withRecipeId(getRecipeId()).withScope(m.getMarkers().findFirst(Range.class).get()); + FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()).withRecipeId(getRecipeId()).withScope(m.getMarkers().findFirst(Range.class).get()); 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 0aa97f4d1..252ae5b50 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 @@ -12,17 +12,22 @@ package org.springframework.ide.vscode.commons.rewrite.config; import org.openrewrite.ExecutionContext; import org.openrewrite.java.JavaVisitor; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.commons.java.IJavaProject; public interface RecipeCodeActionDescriptor { + default String getId() { + return getClass().getName(); + } + String getRecipeId(); String getLabel(RecipeScope s); RecipeScope[] getScopes(); - JavaVisitor getMarkerVisitor(); + 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 0c1eca018..08e05b91c 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 @@ -16,11 +16,14 @@ public class FixAssistMarker implements Marker { private String recipeId; + private String descriptorId; + private Map parameters = Collections.emptyMap(); - public FixAssistMarker(UUID id) { + public FixAssistMarker(UUID id, String descriptorId) { super(); this.id = id; + this.descriptorId = descriptorId; } @Override @@ -62,9 +65,13 @@ public class FixAssistMarker implements Marker { return parameters; } + public String getDescriptorId() { + return descriptorId; + } + @Override public int hashCode() { - return Objects.hash(id); + return Objects.hash(descriptorId, id, parameters, recipeId, scope); } @Override @@ -76,7 +83,10 @@ public class FixAssistMarker implements Marker { if (getClass() != obj.getClass()) return false; FixAssistMarker other = (FixAssistMarker) obj; - return Objects.equals(id, other.id); + 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); } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java index ebffdafa2..161a1c5c4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java @@ -182,6 +182,8 @@ public class BootLanguageServerInitializer implements InitializingBean { server.getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles); server.getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles); + + springIndexer.onUpdate(v -> reconcile()); } private void reconcile() { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java index 8ccde0da0..f0be33256 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java @@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -62,6 +63,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.FutureProjectF import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; +import org.springframework.ide.vscode.commons.languageserver.util.ListenerList; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService; @@ -97,8 +99,8 @@ public class SpringSymbolIndex implements InitializingBean { private final ExecutorService updateQueue = Executors.newSingleThreadExecutor(); private SpringIndexer[] indexers; - - + private ListenerList listeners = new ListenerList(); + private static final Logger log = LoggerFactory.getLogger(SpringSymbolIndex.class); private final Listener projectListener = new Listener() { @@ -310,7 +312,9 @@ public class SpringSymbolIndex implements InitializingBean { futures[i] = CompletableFuture.runAsync(initializeItem, this.updateQueue); } - return CompletableFuture.allOf(futures); + CompletableFuture future = CompletableFuture.allOf(futures); + future.thenAccept(v -> listeners.fire(v)); + return future; } } else { return deleteProject(project); @@ -388,7 +392,9 @@ public class SpringSymbolIndex implements InitializingBean { } } - return CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + CompletableFuture future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + future.thenAccept(v -> listeners.fire(v)); + return future; } } @@ -445,7 +451,9 @@ public class SpringSymbolIndex implements InitializingBean { } } } - return CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + CompletableFuture future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + future.thenAccept(v -> listeners.fire(v)); + return future; } } @@ -512,7 +520,9 @@ public class SpringSymbolIndex implements InitializingBean { futures.add(CompletableFuture.runAsync(deleteItems, this.updateQueue)); } - return CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + CompletableFuture future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + future.thenAccept(v -> listeners.fire(v)); + return future; } catch (Exception e) { log.error("", e); @@ -869,4 +879,8 @@ public class SpringSymbolIndex implements InitializingBean { } } + + public void onUpdate(Consumer listener) { + listeners.add(listener); + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java index 9546c36d6..4a992e46f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot3JavaProblemType.java @@ -25,7 +25,9 @@ public enum Boot3JavaProblemType implements ProblemType { JAVA_CONCRETE_BEAN_TYPE(WARNING, "Bean definition should have precise type for Spring 6 AOT", "Not precise bean defintion type"), - JAVA_BEAN_NOT_REGISTERED_IN_AOT(WARNING, "'BeanPostProcessor' behaviour is ignored in Spring 6 AOT", "'BeanPostProcessor' behaviour is ignored in AOT"); + JAVA_BEAN_POST_PROCESSOR_IGNORED_IN_AOT(WARNING, "'BeanPostProcessor' behaviour is ignored in Spring 6 AOT", "'BeanPostProcessor' behaviour is ignored in AOT"), + + JAVA_BEAN_NOT_REGISTERED_IN_AOT(WARNING, "Not registered as Bean", "Not registered as a Bean"); private final ProblemSeverity defaultSeverity; private String description; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolAddOnInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolAddOnInformation.java index 7c15d2dbc..7c421563c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolAddOnInformation.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolAddOnInformation.java @@ -18,13 +18,19 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; public class BeansSymbolAddOnInformation implements SymbolAddOnInformation { private final String beanID; + private final String beanType; - public BeansSymbolAddOnInformation(String beanID) { + public BeansSymbolAddOnInformation(String beanID, String beanType) { this.beanID = beanID; + this.beanType = beanType; } public String getBeanID() { return beanID; } - + + public String getBeanType() { + return beanType; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java index ade3ac58d..a52307e98 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java @@ -63,16 +63,16 @@ public class BeansSymbolProvider extends AbstractSymbolProvider { if (isMethodAbstract(node)) return; boolean isFunction = isFunctionBean(node); - String beanType = getBeanType(node); + ITypeBinding beanType = getBeanType(node); String markerString = getAnnotations(node); for (Tuple2 nameAndRegion : getBeanNames(node, doc)) { try { EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation( new WorkspaceSymbol( - beanLabel(isFunction, nameAndRegion.getT1(), beanType, "@Bean" + markerString), + beanLabel(isFunction, nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString), SymbolKind.Interface, Either.forLeft(new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2())))), - new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(nameAndRegion.getT1())} + new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(nameAndRegion.getT1(), beanType.getQualifiedName())} ); context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol)); @@ -86,16 +86,16 @@ public class BeansSymbolProvider extends AbstractSymbolProvider { @Override protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) { // this checks function beans that are defined as implementations of Function interfaces - Tuple3 functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc); + Tuple3 functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc); if (functionBean != null) { try { WorkspaceSymbol symbol = new WorkspaceSymbol( - beanLabel(true, functionBean.getT1(), functionBean.getT2(), null), + beanLabel(true, functionBean.getT1(), functionBean.getT2().getName(), null), SymbolKind.Interface, Either.forLeft(new Location(doc.getUri(), doc.toRange(functionBean.getT3())))); context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), - new EnhancedSymbolInformation(symbol, new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(functionBean.getT1())}))); + new EnhancedSymbolInformation(symbol, new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(functionBean.getT1(), functionBean.getT2().getQualifiedName())}))); } catch (BadLocationException e) { log.error("", e); @@ -153,12 +153,11 @@ public class BeansSymbolProvider extends AbstractSymbolProvider { return literals.build(); } - protected String getBeanType(Annotation node) { + protected ITypeBinding getBeanType(Annotation node) { ASTNode parent = node.getParent(); if (parent instanceof MethodDeclaration) { MethodDeclaration method = (MethodDeclaration) parent; - String returnType = method.getReturnType2().resolveBinding().getName(); - return returnType; + return method.getReturnType2().resolveBinding(); } return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java index 1b5de3c15..b12efe1e0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java @@ -59,13 +59,13 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider { .map(ITypeBinding::getName) .collect(Collectors.toList()); String beanName = getBeanName(node); - String beanType = getBeanType(node); + ITypeBinding beanType = getBeanType(node); WorkspaceSymbol symbol = new WorkspaceSymbol( - beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, beanType), SymbolKind.Interface, + beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, beanType.getName()), SymbolKind.Interface, Either.forLeft(new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())))); - SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(beanName)}; + SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(beanName, beanType.getQualifiedName())}; return new EnhancedSymbolInformation(symbol, addon); } @@ -108,12 +108,11 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider { return null; } - private String getBeanType(Annotation node) { + private ITypeBinding getBeanType(Annotation node) { ASTNode parent = node.getParent(); if (parent instanceof TypeDeclaration) { TypeDeclaration type = (TypeDeclaration) parent; - String returnType = type.resolveBinding().getName(); - return returnType; + return type.resolveBinding(); } return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java index 060bb29ea..0c9b63d10 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java @@ -43,15 +43,15 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider { @Override protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) { // this checks spring data repository beans that are defined as extensions of the repository interface - Tuple4 repositoryBean = getRepositoryBean(typeDeclaration, doc); + Tuple4 repositoryBean = getRepositoryBean(typeDeclaration, doc); if (repositoryBean != null) { try { WorkspaceSymbol symbol = new WorkspaceSymbol( - beanLabel(true, repositoryBean.getT1(), repositoryBean.getT2(), repositoryBean.getT3()), + beanLabel(true, repositoryBean.getT1(), repositoryBean.getT2().getName(), repositoryBean.getT3()), SymbolKind.Interface, Either.forLeft(new Location(doc.getUri(), doc.toRange(repositoryBean.getT4())))); - SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(repositoryBean.getT1())}; + SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(repositoryBean.getT1(), repositoryBean.getT2().getQualifiedName())}; EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(symbol, addon); context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol)); @@ -76,7 +76,7 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider { return symbolLabel.toString(); } - private static Tuple4 getRepositoryBean(TypeDeclaration typeDeclaration, TextDocument doc) { + private static Tuple4 getRepositoryBean(TypeDeclaration typeDeclaration, TextDocument doc) { ITypeBinding resolvedType = typeDeclaration.resolveBinding(); if (resolvedType != null) { @@ -87,7 +87,7 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider { } } - private static Tuple4 getRepositoryBean(TypeDeclaration typeDeclaration, TextDocument doc, + private static Tuple4 getRepositoryBean(TypeDeclaration typeDeclaration, TextDocument doc, ITypeBinding resolvedType) { ITypeBinding[] interfaces = resolvedType.getInterfaces(); @@ -102,7 +102,6 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider { if (Constants.REPOSITORY_TYPE.equals(simplifiedType)) { String beanName = getBeanName(typeDeclaration); - String beanType = resolvedInterface.getName(); String domainType = null; if (resolvedInterface.isParameterizedType()) { @@ -113,10 +112,10 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider { } DocumentRegion region = ASTUtils.nodeRegion(doc, typeDeclaration.getName()); - return Tuples.of(beanName, beanType, domainType, region); + return Tuples.of(beanName, resolvedInterface, domainType, region); } else { - Tuple4 result = getRepositoryBean(typeDeclaration, doc, resolvedInterface); + Tuple4 result = getRepositoryBean(typeDeclaration, doc, resolvedInterface); if (result != null) { return result; } 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 367d5fa95..b5652eee6 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 @@ -16,9 +16,10 @@ import org.springframework.ide.vscode.boot.java.rewrite.codeaction.AutowiredFiel 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.BeanPostProcessingIgnoreInAotProblem; 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; +import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NotRegisteredBeansProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.PreciseBeanTypeProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.UnnecessarySpringExtensionProblem; import org.springframework.ide.vscode.commons.rewrite.config.CodeActionRepository; @@ -44,7 +45,8 @@ public class BootCodeActionRepository extends CodeActionRepository { new NoAutowiredOnConstructorProblem(), new UnnecessarySpringExtensionProblem(), new PreciseBeanTypeProblem(), - new BeanPostProcessingIgnoreInAotProblem() + new BeanPostProcessingIgnoreInAotProblem(), + new NotRegisteredBeansProblem() ); } 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 5bfa293c3..9712c3968 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 @@ -156,7 +156,7 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler { 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.getRecipeId()); + 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)) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java index 9d3a3279b..fa244cdc7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java @@ -49,12 +49,14 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.Validated; import org.openrewrite.config.RecipeDescriptor; import org.openrewrite.config.YamlResourceLoader; -import org.openrewrite.internal.RecipeIntrospectionUtils; import org.openrewrite.java.JavaParser; import org.openrewrite.java.tree.J.CompilationUnit; import org.openrewrite.maven.MavenParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IClasspathUtil; @@ -77,7 +79,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; -public class RewriteRecipeRepository { +public class RewriteRecipeRepository implements ApplicationContextAware { private static final String CMD_REWRITE_RELOAD = "sts/rewrite/reload"; private static final String CMD_REWRITE_EXECUTE = "sts/rewrite/execute"; @@ -101,6 +103,8 @@ public class RewriteRecipeRepository { final private ListenerList loadListeners; + private ApplicationContext applicationContext; + private CompletableFuture loaded; private Set scanFiles = Collections.emptySet(); @@ -235,7 +239,7 @@ public class RewriteRecipeRepository { public RecipeSpringJavaProblemDescriptor getProblemRecipeDescriptor(String id) { for (RecipeSpringJavaProblemDescriptor d : javaProblemDescriptors) { - if (id.equals(d.getRecipeId())) { + if (id.equals(d.getId())) { return d; } } @@ -244,7 +248,7 @@ public class RewriteRecipeRepository { public RecipeCodeActionDescriptor getCodeActionRecipeDescriptor(String id) { for (RecipeCodeActionDescriptor d : codeActionDescriptors) { - if (id.equals(d.getRecipeId())) { + if (id.equals(d.getId())) { return d; } } @@ -272,13 +276,9 @@ public class RewriteRecipeRepository { public CompilationUnit mark(List descriptors, CompilationUnit compilationUnit) { CompilationUnit cu = compilationUnit; for (RecipeCodeActionDescriptor d : descriptors) { - Recipe recipe = getRecipe(d.getRecipeId()).orElse(null); - if (recipe != null) { - TreeVisitor isApplicableVisitor = RecipeIntrospectionUtils.recipeSingleSourceApplicableTest(recipe); - TreeVisitor markVisitor = d.getMarkerVisitor(); - if (markVisitor != null && (isApplicableVisitor == null || isApplicableVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("", e))) != cu)) { - cu = (CompilationUnit) markVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("", e))); - } + TreeVisitor markVisitor = d.getMarkerVisitor(applicationContext); + if (markVisitor != null) { + cu = (CompilationUnit) markVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("", e))); } } return cu; @@ -447,6 +447,11 @@ public class RewriteRecipeRepository { public void onRecipesLoaded(Consumer l) { loadListeners.add(l); } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } // private static Recipe convert(Recipe r, RecipeDescriptor d) { // try { 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 9605d9e91..dfec031cf 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 @@ -101,21 +101,21 @@ public class RewriteReconciler implements JavaReconciler { if (astNode != null) { Range range = astNode.getMarkers().findFirst(Range.class).orElse(null); if (range != null) { - RecipeSpringJavaProblemDescriptor recipeFixDescriptor = recipeRepo.getProblemRecipeDescriptor(m.getRecipeId()); - if (recipeFixDescriptor != null && recipeFixDescriptor.getScopes() != null && recipeRepo.getRecipe(recipeFixDescriptor.getRecipeId()).isPresent()) { - return List.of(createProblemFromScope(doc, recipeFixDescriptor, m, range)); + RecipeSpringJavaProblemDescriptor recipeFixDescriptor = recipeRepo.getProblemRecipeDescriptor(m.getDescriptorId()); + if (recipeFixDescriptor != null) { + return List.of(createProblem(doc, recipeFixDescriptor, m, range)); } } } return Collections.emptyList(); } - private ReconcileProblemImpl createProblemFromScope(IDocument doc, RecipeSpringJavaProblemDescriptor recipeFixDescriptor, + private ReconcileProblemImpl createProblem(IDocument doc, RecipeSpringJavaProblemDescriptor recipeFixDescriptor, FixAssistMarker m, Range range) { 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) { + if (quickfixType != null && m.getRecipeId() != null && recipeRepo.getRecipe(recipeFixDescriptor.getRecipeId()).isPresent()) { for (RecipeScope s : recipeFixDescriptor.getScopes()) { problem.addQuickfix(new QuickfixData<>( quickfixType, 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 67f2a14cc..541396f29 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 @@ -30,6 +30,7 @@ import org.openrewrite.java.tree.J.VariableDeclarations; 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; @@ -59,7 +60,7 @@ public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeC } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor<>() { @Override @@ -75,7 +76,7 @@ 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(); - FixAssistMarker marker = new FixAssistMarker(Tree.randomId()) + FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()) .withRecipeId(getRecipeId()) .withScope(classDeclaration.getMarkers().findFirst(Range.class).get()) .withParameters(Map.of("classFqName", fqType.getFullyQualifiedName(), "fieldName", 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 index dc9f895ab..409f45da1 100644 --- 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 @@ -19,6 +19,7 @@ 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; @@ -49,7 +50,7 @@ public class BeanMethodsNotPublicCodeAction implements RecipeCodeActionDescripto } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @Override @@ -59,7 +60,7 @@ public class BeanMethodsNotPublicCodeAction implements RecipeCodeActionDescripto 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()) + FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) .withRecipeId(ID) .withScope(m.getMarkers().findFirst(Range.class).get()); m = m.withModifiers(ListUtils.map(m.getModifiers(), modifier -> { 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 96dd60d89..97824e898 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 @@ -19,6 +19,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J; 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; @@ -46,13 +47,13 @@ public class NoRequestMappingAnnotationCodeAction implements RecipeCodeActionDes } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @Override 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) { - FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId()) + FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) .withRecipeId(getRecipeId()) .withScope(a.getMarkers().findFirst(Range.class).get()); 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/codeaction/UnnecessarySpringExtensionCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/UnnecessarySpringExtensionCodeAction.java index bc744550a..8da1fd82c 100644 --- 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 @@ -25,6 +25,7 @@ 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; @@ -70,7 +71,7 @@ public class UnnecessarySpringExtensionCodeAction implements RecipeCodeActionDes } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor<>() { @Override @@ -83,7 +84,7 @@ public class UnnecessarySpringExtensionCodeAction implements RecipeCodeActionDes 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()).withRecipeId(ID).withScope(range))); + return a.withMarkers(a.getMarkers().add(new FixAssistMarker(Tree.randomId(), getId()).withRecipeId(ID).withScope(range))); } return a; })); 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 5ebdd5d30..41e30acf6 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 @@ -22,6 +22,7 @@ import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J.ClassDeclaration; import org.openrewrite.java.tree.J.MethodDeclaration; 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; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; @@ -51,7 +52,7 @@ public class BeanPostProcessingIgnoreInAotProblem implements RecipeSpringJavaPro } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { @Override @@ -62,7 +63,7 @@ public class BeanPostProcessingIgnoreInAotProblem implements RecipeSpringJavaPro .filter(MethodDeclaration.class::isInstance).map(MethodDeclaration.class::cast) .filter(BeanPostProcessingIgnoreInAot::isApplicableMethod) .collect(Collectors.toList()); - FixAssistMarker marker = new FixAssistMarker(Tree.randomId()) + FixAssistMarker marker = new FixAssistMarker(Tree.randomId(), getId()) .withRecipeId(getRecipeId()) .withScope(classDecl.getMarkers().findFirst(Range.class).orElse(null)); if (methods.isEmpty()) { @@ -89,7 +90,7 @@ public class BeanPostProcessingIgnoreInAotProblem implements RecipeSpringJavaPro @Override public ProblemType getProblemType() { - return Boot3JavaProblemType.JAVA_BEAN_NOT_REGISTERED_IN_AOT; + return Boot3JavaProblemType.JAVA_BEAN_POST_PROCESSOR_IGNORED_IN_AOT; } } 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 b299ce1b0..4ea58a918 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 @@ -24,6 +24,7 @@ import org.openrewrite.java.tree.JavaType.FullyQualified; import org.openrewrite.marker.Range; import org.openrewrite.java.tree.Statement; import org.openrewrite.java.tree.TypeUtils; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -53,7 +54,7 @@ public class NoAutowiredOnConstructorProblem implements RecipeSpringJavaProblemD } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor() { public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext context) { J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, context); @@ -76,7 +77,7 @@ public class NoAutowiredOnConstructorProblem implements RecipeSpringJavaProblemD return s; } MethodDeclaration constructor = (MethodDeclaration) s; - FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId()) + FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()) .withRecipeId(ID) .withScope(getCursor().firstEnclosing(ClassDeclaration.class).getMarkers().findFirst(Range.class).get()); constructor = constructor.withLeadingAnnotations(ListUtils.map(constructor.getLeadingAnnotations(), a -> { 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 new file mode 100644 index 000000000..874d6ac6a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NotRegisteredBeansProblem.java @@ -0,0 +1,104 @@ +package org.springframework.ide.vscode.boot.java.rewrite.reconcile; + +import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; + +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.WorkspaceSymbol; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Tree; +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.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; +import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation; +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() { + + @Override + public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) { + ClassDeclaration c = super.visitClassDeclaration(classDecl, p); + FullyQualified type = c.getType(); + if (type != null) { + String beanClassName = type.getFullyQualifiedName(); + boolean applicable = AOT_BEANS.stream().filter(fqName -> TypeUtils.isAssignableTo(fqName, type)).findFirst().isPresent(); + if (applicable) { + SpringSymbolIndex index = applicationContext.getBean(SpringSymbolIndex.class); + List beanSymbols = index.getSymbols(data -> { + SymbolAddOnInformation[] additionalInformation = data.getAdditionalInformation(); + if (additionalInformation != null) { + 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()); + } + } + } + return false; + }).limit(1).collect(Collectors.toList()); + if (beanSymbols.isEmpty()) { + return c = c.withName(c.getName().withMarkers(c.getName().getMarkers().add(new FixAssistMarker(Tree.randomId(), getId())))); + } + } + } + return c; + } + + }; + } + + @Override + public boolean isApplicable(IJavaProject project) { + return springBootVersionGreaterOrEqual(3, 0, 0).test(project); + } + + @Override + public ProblemType getProblemType() { + return Boot3JavaProblemType.JAVA_BEAN_NOT_REGISTERED_IN_AOT; + } + +} 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 91b71dd47..f0b936e16 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 @@ -22,6 +22,7 @@ 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.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; @@ -51,7 +52,7 @@ public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor } @Override - public JavaVisitor getMarkerVisitor() { + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { return new JavaIsoVisitor<>() { public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) { @@ -62,7 +63,7 @@ public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor 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()).withScope(m.getMarkers().findFirst(Range.class).get()).withRecipeId(getRecipeId())))); + new FixAssistMarker(Tree.randomId(), getId()).withScope(m.getMarkers().findFirst(Range.class).get()).withRecipeId(getRecipeId())))); } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/FunctionUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/FunctionUtils.java index 283b4da34..d73437d7f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/FunctionUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/FunctionUtils.java @@ -34,7 +34,7 @@ public class FunctionUtils { public static final String FUNCTION_CONSUMER_TYPE = Consumer.class.getName(); public static final String FUNCTION_SUPPLIER_TYPE = Supplier.class.getName(); - public static Tuple3 getFunctionBean(TypeDeclaration typeDeclaration, TextDocument doc) { + public static Tuple3 getFunctionBean(TypeDeclaration typeDeclaration, TextDocument doc) { ITypeBinding resolvedType = typeDeclaration.resolveBinding(); if (resolvedType != null && !resolvedType.isInterface() && !isAbstractClass(typeDeclaration, resolvedType)) { @@ -45,7 +45,7 @@ public class FunctionUtils { } } - private static Tuple3 getFunctionBean(TypeDeclaration typeDeclaration, TextDocument doc, + private static Tuple3 getFunctionBean(TypeDeclaration typeDeclaration, TextDocument doc, ITypeBinding resolvedType) { ITypeBinding[] interfaces = resolvedType.getInterfaces(); @@ -61,13 +61,12 @@ public class FunctionUtils { if (FUNCTION_FUNCTION_TYPE.equals(simplifiedType) || FUNCTION_CONSUMER_TYPE.equals(simplifiedType) || FUNCTION_SUPPLIER_TYPE.equals(simplifiedType)) { String beanName = getBeanName(typeDeclaration); - String beanType = resolvedInterface.getName(); DocumentRegion region = ASTUtils.nodeRegion(doc, typeDeclaration.getName()); - return Tuples.of(beanName, beanType, region); + return Tuples.of(beanName, resolvedInterface, region); } else { - Tuple3 result = getFunctionBean(typeDeclaration, doc, resolvedInterface); + Tuple3 result = getFunctionBean(typeDeclaration, doc, resolvedInterface); if (result != null) { return result; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java index 055691bed..890b0181d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java @@ -47,6 +47,7 @@ public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNa int symbolEnd = 0; String beanClass = null; + String fqBeanClass = null; List attributes = node.getAttributeNodes(); for (DOMAttr attribute : attributes) { @@ -59,8 +60,8 @@ public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNa symbolEnd = attribute.getEnd(); } else if (name != null && name.equals("class")) { - String value = attribute.getValue(); - beanClass = value.substring(value.lastIndexOf(".") + 1); + fqBeanClass = attribute.getValue(); + beanClass = fqBeanClass.substring(fqBeanClass.lastIndexOf(".") + 1); if (symbolStart == 0 && symbolEnd == 0) { symbolStart = attribute.getStart(); @@ -89,7 +90,7 @@ public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNa } WorkspaceSymbol symbol = new WorkspaceSymbol("@+ '" + beanID + "' " + beanClass, SymbolKind.Interface, Either.forLeft(new Location(docURI, range))); - SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(beanID)}; + SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(beanID, fqBeanClass)}; EnhancedSymbolInformation fullSymbol = new EnhancedSymbolInformation(symbol, addon);