GH-1499: re-validating bean registrars now when config classes change their import annotation expressions
This commit is contained in:
@@ -235,7 +235,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
|
||||
@Override
|
||||
public void removeSymbols(IJavaProject project, String docURI) {
|
||||
SpringSymbolIndex.this.removeSymbolsByDoc(project, docURI);
|
||||
springIndex.removeElements(project.getElementName(), docURI);
|
||||
// springIndex.removeElements(project.getElementName(), docURI);
|
||||
|
||||
// TODO remove diagnostics ?!? maybe, maybe not
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.index;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -249,4 +250,23 @@ public class SpringMetamodelIndex {
|
||||
return (Bean[]) result.toArray(new Bean[result.size()]);
|
||||
}
|
||||
|
||||
public static <T extends SpringIndexElement> List<T> getNodesOfType(Class<T> type, Collection<SpringIndexElement> rootNodes) {
|
||||
List<T> result = new ArrayList<>();
|
||||
|
||||
ArrayDeque<SpringIndexElement> elementsToVisit = new ArrayDeque<>();
|
||||
elementsToVisit.addAll(rootNodes);
|
||||
|
||||
while (!elementsToVisit.isEmpty()) {
|
||||
SpringIndexElement element = elementsToVisit.pop();
|
||||
|
||||
if (type.isInstance(element)) {
|
||||
result.add(type.cast(element));
|
||||
}
|
||||
|
||||
elementsToVisit.addAll(element.getChildren());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.springframework.ide.vscode.boot.java.embedded.lang.EmbeddedLanguageSnippet;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcilingContext;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
|
||||
public class JdtCronReconciler implements JdtAstReconciler {
|
||||
@@ -41,14 +41,13 @@ public class JdtCronReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu,
|
||||
IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) {
|
||||
return new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
EmbeddedLanguageSnippet e = JdtCronVisitorUtils.extractCron(node);
|
||||
if (e != null) {
|
||||
cronReconciler.reconcile(e.getText(), e::toSingleJavaRange, problemCollector);
|
||||
cronReconciler.reconcile(e.getText(), e::toSingleJavaRange, context.getProblemCollector());
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ import org.springframework.ide.vscode.boot.java.data.jpa.queries.JdtQueryVisitor
|
||||
import org.springframework.ide.vscode.boot.java.embedded.lang.AntlrReconcilerWithSpel;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcilingContext;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
|
||||
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.parser.mysql.MySqlLexer;
|
||||
import org.springframework.ide.vscode.parser.mysql.MySqlParser;
|
||||
@@ -54,7 +54,7 @@ public class QueryJdtAstReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) throws RequiredCompleteAstException {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) throws RequiredCompleteAstException {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(cu);
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -63,7 +63,7 @@ public class QueryJdtAstReconciler implements JdtAstReconciler {
|
||||
EmbeddedQueryExpression q = JdtQueryVisitorUtils.extractQueryExpression(annotationHierarchies, node);
|
||||
if (q != null) {
|
||||
Optional<Reconciler> reconcilerOpt = q.isNative() ? getSqlReconciler(project) : Optional.of(getQueryReconciler(project));
|
||||
reconcilerOpt.ifPresent(r -> r.reconcile(q.query().getText(), q.query()::toSingleJavaRange, problemCollector));
|
||||
reconcilerOpt.ifPresent(r -> r.reconcile(q.query().getText(), q.query()::toSingleJavaRange, context.getProblemCollector()));
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class QueryJdtAstReconciler implements JdtAstReconciler {
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
EmbeddedQueryExpression q = JdtQueryVisitorUtils.extractQueryExpression(annotationHierarchies, node);
|
||||
if (q != null) {
|
||||
getQueryReconciler(project).reconcile(q.query().getText(), q.query()::toSingleJavaRange, problemCollector);
|
||||
getQueryReconciler(project).reconcile(q.query().getText(), q.query()::toSingleJavaRange, context.getProblemCollector());
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class QueryJdtAstReconciler implements JdtAstReconciler {
|
||||
public boolean visit(MethodInvocation node) {
|
||||
EmbeddedQueryExpression q = JdtQueryVisitorUtils.extractQueryExpression(node);
|
||||
if (q != null) {
|
||||
getQueryReconciler(project).reconcile(q.query().getText(), q.query()::toSingleJavaRange, problemCollector);
|
||||
getQueryReconciler(project).reconcile(q.query().getText(), q.query()::toSingleJavaRange, context.getProblemCollector());
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodInvocation;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
@@ -34,9 +33,9 @@ public abstract class AbstractSecurityLamdaDslReconciler implements JdtAstReconc
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
if (isCompleteAst) {
|
||||
if (context.isCompleteAst()) {
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -59,7 +58,7 @@ public abstract class AbstractSecurityLamdaDslReconciler implements JdtAstReconc
|
||||
ReconcileUtils.buildLabel(getFixLabel(), RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.springframework.ide.vscode.commons.Version;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
@@ -65,7 +64,7 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -85,7 +84,7 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile
|
||||
ReconcileUtils.buildLabel(FIX_LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)));
|
||||
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.springframework.ide.vscode.commons.Version;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -57,9 +56,9 @@ public class AuthorizeHttpRequestsReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
if (isCompleteAst) {
|
||||
if (context.isCompleteAst()) {
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -82,7 +81,7 @@ public class AuthorizeHttpRequestsReconciler implements JdtAstReconciler {
|
||||
ReconcileUtils.buildLabel(AUTHORIZE_REQUESTS_FIX_LABEL,
|
||||
RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchie
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -67,7 +66,7 @@ public class AutowiredFieldIntoConstructorParameterReconciler implements JdtAstR
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
Path sourceFile = Paths.get(docUri);
|
||||
// Check if source file belongs to non-test java sources folder
|
||||
if (IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(project.getClasspath())
|
||||
@@ -91,14 +90,14 @@ public class AutowiredFieldIntoConstructorParameterReconciler implements JdtAstR
|
||||
String fieldName = variableDeclarationFragment.getName().getIdentifier();
|
||||
|
||||
if (constructors.isEmpty()) {
|
||||
problemCollector.accept(createProblem(cu, field, fieldName, docUri));
|
||||
context.getProblemCollector().accept(createProblem(cu, field, fieldName, docUri));
|
||||
} else if (constructors.size() == 1) {
|
||||
if (!isCompleteAst) {
|
||||
if (!context.isCompleteAst()) {
|
||||
throw new RequiredCompleteAstException();
|
||||
}
|
||||
if (!isAssigningField(constructors.get(0), variableDeclarationFragment.resolveBinding(),
|
||||
fieldName)) {
|
||||
problemCollector.accept(createProblem(cu, field, fieldName, docUri));
|
||||
context.getProblemCollector().accept(createProblem(cu, field, fieldName, docUri));
|
||||
}
|
||||
} else {
|
||||
List<MethodDeclaration> autowiredConstructors = constructors.stream()
|
||||
@@ -106,11 +105,11 @@ public class AutowiredFieldIntoConstructorParameterReconciler implements JdtAstR
|
||||
Annotations.AUTOWIRED, true) != null)
|
||||
.limit(2).collect(Collectors.toList());
|
||||
if (autowiredConstructors.size() == 1) {
|
||||
if (!isCompleteAst) {
|
||||
if (!context.isCompleteAst()) {
|
||||
throw new RequiredCompleteAstException();
|
||||
} else if (!isAssigningField(autowiredConstructors.get(0),
|
||||
variableDeclarationFragment.resolveBinding(), fieldName)) {
|
||||
problemCollector.accept(createProblem(cu, field, fieldName, docUri));
|
||||
context.getProblemCollector().accept(createProblem(cu, field, fieldName, docUri));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,14 +66,14 @@ public class BeanMethodNotPublicReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
visitAnnotation(project, cu, docUri, node, problemCollector);
|
||||
visitAnnotation(project, cu, docUri, node, context.getProblemCollector());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return super.visit(node);
|
||||
@@ -82,7 +82,7 @@ public class BeanMethodNotPublicReconciler implements JdtAstReconciler {
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
try {
|
||||
visitAnnotation(project, cu, docUri, node, problemCollector);
|
||||
visitAnnotation(project, cu, docUri, node, context.getProblemCollector());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return super.visit(node);
|
||||
@@ -91,7 +91,7 @@ public class BeanMethodNotPublicReconciler implements JdtAstReconciler {
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
try {
|
||||
visitAnnotation(project, cu, docUri, node, problemCollector);
|
||||
visitAnnotation(project, cu, docUri, node, context.getProblemCollector());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return super.visit(node);
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -56,7 +55,7 @@ public class BeanPostProcessingIgnoreInAotReconciler implements JdtAstReconciler
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -73,7 +72,7 @@ public class BeanPostProcessingIgnoreInAotReconciler implements JdtAstReconciler
|
||||
}
|
||||
}
|
||||
if (foundMethod != null) {
|
||||
if (isCompleteAst) {
|
||||
if (context.isCompleteAst()) {
|
||||
AtomicBoolean returnsTrue = new AtomicBoolean(false);
|
||||
foundMethod.accept(new ASTVisitor() {
|
||||
@Override
|
||||
@@ -99,7 +98,7 @@ public class BeanPostProcessingIgnoreInAotReconciler implements JdtAstReconciler
|
||||
.withRangeScope(ReconcileUtils.createOpenRewriteRange(cu, typeDecl, null))
|
||||
.withRecipeScope(RecipeScope.NODE)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -33,13 +34,16 @@ import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.ImportBeanRegistrarInConfigRecipe;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
|
||||
public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler {
|
||||
|
||||
@@ -63,8 +67,7 @@ public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu,
|
||||
IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) {
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -75,11 +78,14 @@ public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler {
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is not a bean registrar, but maybe a config class that has changed and is now importing a bean registrar
|
||||
// so we need to mark the bean registrar for reconciling
|
||||
if (ASTUtils.findInTypeHierarchy(type, Set.of(Annotations.BEAN_REGISTRAR_INTERFACE)) == null) {
|
||||
identifyPossibleRegistrarsForReconciling(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isIndexComplete) {
|
||||
if (!context.isIndexComplete()) {
|
||||
throw new RequiredCompleteIndexException();
|
||||
}
|
||||
|
||||
@@ -99,7 +105,9 @@ public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler {
|
||||
}
|
||||
}
|
||||
|
||||
if (configBeans.isEmpty() || !isImportedBeanRegistrarInConfig(configBeans, type)) {
|
||||
List<String> importingBeanRegistrarConfigs = getImportedBeanRegistrarConfigs(configBeans, type);
|
||||
if (configBeans.isEmpty() || importingBeanRegistrarConfigs.size() == 0) {
|
||||
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), "Bean not registered", node.getName().getStartPosition(), node.getName().getLength());
|
||||
List<FixDescriptor> fixes = configBeans.stream()
|
||||
.filter(b -> b.getLocation() != null && b.getLocation().getUri() != null)
|
||||
@@ -111,7 +119,18 @@ public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler {
|
||||
.withRecipeScope(RecipeScope.FILE)
|
||||
).toList();
|
||||
ReconcileUtils.setRewriteFixes(registry, problem, fixes);
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
|
||||
// record dependencies on types where we found import annotations for this bean registrar
|
||||
// mark this file
|
||||
|
||||
}
|
||||
else {
|
||||
// record dependencies on types where we found import annotations for this bean registrar
|
||||
for (String typeOfConfigClassWithImport : importingBeanRegistrarConfigs) {
|
||||
context.addDependency(typeOfConfigClassWithImport);
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -119,12 +138,65 @@ public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler {
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isImportedBeanRegistrarInConfig(List<Bean> configBeans, ITypeBinding beanRegType) {
|
||||
private List<String> getImportedBeanRegistrarConfigs(List<Bean> configBeans, ITypeBinding beanRegType) {
|
||||
return configBeans.stream()
|
||||
.flatMap(bean -> Arrays.stream(bean.getAnnotations())) // look into annotations on this bean definition
|
||||
.filter(configBean -> isImportingBeanRegistrar(configBean, beanRegType))
|
||||
.map(configBean -> configBean.getType())
|
||||
.toList();
|
||||
}
|
||||
|
||||
private boolean isImportingBeanRegistrar(Bean configBean, ITypeBinding beanRegType) {
|
||||
if (configBean.getAnnotations() == null) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return Arrays.stream(configBean.getAnnotations())
|
||||
.filter(annotation -> Annotations.IMPORT.equals(annotation.getAnnotationType())) // look into @Import annotations only
|
||||
.flatMap(annotation -> annotation.getAttributes().get("value") == null ? Stream.empty() : Arrays.stream(annotation.getAttributes().get("value"))) // look into the attribute values of "value" attribute
|
||||
.anyMatch(annotationValue -> annotationValue.getName().equals(beanRegType.getQualifiedName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is basically the reverse check: it tries to identify possible bean registrars that need to be
|
||||
* reconciled because the type this reconciler is currently looking at refers to it or has referred to it.
|
||||
*/
|
||||
protected void identifyPossibleRegistrarsForReconciling(ReconcilingContext context) {
|
||||
List<SpringIndexElement> createdIndexElements = context.getCreatedIndexElements();
|
||||
List<SpringIndexElement> previuosIndexElements = springIndex.getDocument(context.getDocURI()) != null ?
|
||||
springIndex.getDocument(context.getDocURI()).getChildren() : Collections.emptyList();
|
||||
|
||||
Set<String> importedTypesDelta = getImportAnnotationTypesDelta(createdIndexElements, previuosIndexElements);
|
||||
|
||||
Arrays.stream(springIndex.getBeans())
|
||||
.filter(bean -> bean.isTypeCompatibleWith(Annotations.BEAN_REGISTRAR_INTERFACE))
|
||||
.filter(bean -> importedTypesDelta.contains(bean.getType()))
|
||||
.map(bean -> bean.getLocation().getUri())
|
||||
.map(docURI -> UriUtil.toFileString(docURI))
|
||||
.forEach(file -> context.markForAffetcedFilesIndexing(file));
|
||||
}
|
||||
|
||||
public Set<String> getImportAnnotationTypesDelta(List<SpringIndexElement> updatedElements, List<SpringIndexElement> previousElements) {
|
||||
Set<String> updatedImportedTypes = getImportAnnotationTypes(updatedElements);
|
||||
Set<String> previousImportedTypes = getImportAnnotationTypes(previousElements);
|
||||
|
||||
return Streams.concat(updatedImportedTypes.stream(), previousImportedTypes.stream()
|
||||
.filter(element ->
|
||||
(updatedImportedTypes.contains(element) && !previousImportedTypes.contains(element))
|
||||
|| (!updatedImportedTypes.contains(element) && previousImportedTypes.contains(element))))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Set<String> getImportAnnotationTypes(List<SpringIndexElement> elements) {
|
||||
return elements.stream()
|
||||
.filter(element -> element instanceof Bean)
|
||||
.map(element -> (Bean) element)
|
||||
.flatMap(bean -> Arrays.stream(bean.getAnnotations()))
|
||||
.filter(annotation -> Annotations.IMPORT.equals(annotation.getAnnotationType())) // look into @Import annotations only
|
||||
.flatMap(annotation -> annotation.getAttributes().get("value") == null ? Stream.empty() : Arrays.stream(annotation.getAttributes().get("value"))) // look into the attribute values of "value" attribute
|
||||
.anyMatch(annotationValue -> annotationValue.getName().equals(beanRegType.getQualifiedName()));
|
||||
.map(attributeValue -> attributeValue.getName())
|
||||
.distinct()
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.eclipse.jdt.core.dom.ImportDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleType;
|
||||
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
|
||||
@@ -45,14 +44,14 @@ public class Boot3NotSupportedTypeReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) {
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(ImportDeclaration node) {
|
||||
String fqName = node.getName().getFullyQualifiedName();
|
||||
if (TYPE_FQNAMES.contains(fqName)) {
|
||||
problemCollector.accept(createProblem(fqName, node.getName().getStartPosition(), node.getName().getLength()));
|
||||
context.getProblemCollector().accept(createProblem(fqName, node.getName().getStartPosition(), node.getName().getLength()));
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
@@ -61,7 +60,7 @@ public class Boot3NotSupportedTypeReconciler implements JdtAstReconciler {
|
||||
public boolean visit(SimpleType node) {
|
||||
String fqName = processType(cu, node.getName().getFullyQualifiedName());
|
||||
if (fqName != null) {
|
||||
problemCollector.accept(createProblem(fqName, node.getStartPosition(), node.getLength()));
|
||||
context.getProblemCollector().accept(createProblem(fqName, node.getStartPosition(), node.getLength()));
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchie
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.JavaUtils;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
|
||||
@@ -70,7 +69,7 @@ public class EntityIdForRepoReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(cu);
|
||||
|
||||
return new ASTVisitor() {
|
||||
@@ -129,7 +128,7 @@ public class EntityIdForRepoReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
private void markProblem(ITypeBinding idType, ASTNode node) {
|
||||
problemCollector.accept(new ReconcileProblemImpl(getProblemType(),
|
||||
context.getProblemCollector().accept(new ReconcileProblemImpl(getProblemType(),
|
||||
"Expected Domain ID type is '" + idType.getQualifiedName() + "'", node.getStartPosition(),
|
||||
node.getLength()));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
@@ -74,7 +73,7 @@ public class ImplicitWebAnnotationNamesReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -109,7 +108,7 @@ public class ImplicitWebAnnotationNamesReconciler implements JdtAstReconciler {
|
||||
ReconcileUtils.buildLabel(FIX_LABEL_PLURAL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.net.URI;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
|
||||
public interface JdtAstReconciler {
|
||||
@@ -32,6 +31,6 @@ public interface JdtAstReconciler {
|
||||
|
||||
ProblemType getProblemType();
|
||||
|
||||
ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete);
|
||||
ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022, 2023 VMware, Inc.
|
||||
* Copyright (c) 2022, 2025 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
|
||||
@@ -86,25 +86,28 @@ public class JdtReconciler implements JavaReconciler {
|
||||
final long s = System.currentTimeMillis();
|
||||
URI uri = URI.create(doc.getUri());
|
||||
compilationUnitCache.withCompilationUnit(project, uri, cu -> {
|
||||
|
||||
if (cu != null) {
|
||||
|
||||
try {
|
||||
reconcile(project, URI.create(doc.getUri()), cu, problemCollector, true, true);
|
||||
ReconcilingContext reconcilingContext = new ReconcilingContext(doc.getUri(), problemCollector, true, true, Collections.emptyList());
|
||||
reconcile(project, URI.create(doc.getUri()), cu, reconcilingContext);
|
||||
} catch (RequiredCompleteAstException e) {
|
||||
log.error("Unexpected incomplete AST", e);
|
||||
}
|
||||
// TODO: is the index indeed complete?!? do need to react to complete index exception instead ?!?
|
||||
}
|
||||
|
||||
log.info("reconciling (JDT): " + doc.getUri() + " done in " + (System.currentTimeMillis() - s) + "ms");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public ASTVisitor createCompositeVisitor(IJavaProject project, URI docURI, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst,
|
||||
boolean isIndexComplete) throws RequiredCompleteAstException {
|
||||
public ASTVisitor createCompositeVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) throws RequiredCompleteAstException {
|
||||
CompositeASTVisitor compositeVisitor = new CompositeASTVisitor();
|
||||
|
||||
for (JdtAstReconciler reconciler : getApplicableReconcilers(project)) {
|
||||
ASTVisitor visitor = reconciler.createVisitor(project, docURI, cu, problemCollector, isCompleteAst, isIndexComplete);
|
||||
ASTVisitor visitor = reconciler.createVisitor(project, docURI, cu, context);
|
||||
|
||||
if (visitor != null) {
|
||||
compositeVisitor.add(visitor);
|
||||
@@ -115,7 +118,7 @@ public class JdtReconciler implements JavaReconciler {
|
||||
}
|
||||
|
||||
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete)
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context)
|
||||
throws RequiredCompleteAstException, RequiredCompleteIndexException {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
@@ -125,7 +128,7 @@ public class JdtReconciler implements JavaReconciler {
|
||||
}
|
||||
|
||||
try {
|
||||
ASTVisitor compositeVisitor = createCompositeVisitor(project, docUri, cu, problemCollector, isCompleteAst, isIndexComplete);
|
||||
ASTVisitor compositeVisitor = createCompositeVisitor(project, docUri, cu, context);
|
||||
cu.accept(compositeVisitor);
|
||||
|
||||
// for (JdtAstReconciler reconciler : getApplicableReconcilers(project)) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.ide.vscode.boot.modulith.AppModules;
|
||||
import org.springframework.ide.vscode.boot.modulith.ModulithService;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
|
||||
@@ -52,7 +51,7 @@ public class ModulithTypeReferenceViolationReconciler implements JdtAstReconcile
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
Path sourceFile = Paths.get(docUri);
|
||||
if (IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(project.getClasspath())
|
||||
@@ -81,7 +80,7 @@ public class ModulithTypeReferenceViolationReconciler implements JdtAstReconcile
|
||||
private void validate(ASTNode node, ITypeBinding type) {
|
||||
if (type != null) {
|
||||
appModules.getModuleNotExposingType(packageName, type.getBinaryName()).ifPresent(module -> {
|
||||
problemCollector.accept(new ReconcileProblemImpl(getProblemType(),
|
||||
context.getProblemCollector().accept(new ReconcileProblemImpl(getProblemType(),
|
||||
"Invalid reference to non-exposed type of module '%s'!".formatted(module.name()),
|
||||
node.getStartPosition(), node.getLength()));
|
||||
});
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchie
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -57,7 +56,7 @@ public class NoAutowiredOnConstructorReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(cu);
|
||||
|
||||
return new ASTVisitor() {
|
||||
@@ -91,7 +90,7 @@ public class NoAutowiredOnConstructorReconciler implements JdtAstReconciler {
|
||||
List.of(new FixDescriptor(NoAutowiredOnConstructor.class.getName(), List.of(docUri.toASCIIString()), FIX_LABEL)
|
||||
.withRecipeScope(RecipeScope.NODE)
|
||||
.withRangeScope(ReconcileUtils.createOpenRewriteRange(cu, typeDecl, null))));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
@@ -55,7 +54,7 @@ public class NoRepoAnnotationReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -82,7 +81,7 @@ public class NoRepoAnnotationReconciler implements JdtAstReconciler {
|
||||
ReconcileUtils.buildLabel(FIX_LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -69,7 +68,7 @@ public class NoRequestMappingAnnotationReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -108,12 +107,12 @@ public class NoRequestMappingAnnotationReconciler implements JdtAstReconciler {
|
||||
"Replace all `@RequestMapping` in project '%s' with `@GetMapping`, `@PostMapping`, etc.".formatted(project.getElementName()))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
} else if (SUPPORTED_REQUEST_METHODS == requestMethods) { // the case of no request methods specified
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), PROBLEM_LABEL, a.getStartPosition(), a.getLength());
|
||||
ReconcileUtils.setRewriteFixes(registry, problem,
|
||||
requestMethods.stream().map(m -> createFixDescriptor(uri, range, m)).collect(Collectors.toList()));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class NotRegisteredBeansReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -86,7 +86,7 @@ public class NotRegisteredBeansReconciler implements JdtAstReconciler {
|
||||
Bean[] registeredBeans = springIndex.getBeansWithType(project.getElementName(), beanClassName);
|
||||
|
||||
if (registeredBeans == null || registeredBeans.length == 0) {
|
||||
createProblemAndQuickFixes(project, problemCollector, node, type);
|
||||
createProblemAndQuickFixes(project, context.getProblemCollector(), node, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -57,7 +56,7 @@ public class PreciseBeanTypeReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
final AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(cu);
|
||||
|
||||
return new ASTVisitor() {
|
||||
@@ -72,7 +71,7 @@ public class PreciseBeanTypeReconciler implements JdtAstReconciler {
|
||||
if (methodBinding != null) {
|
||||
boolean isBeanMethod = annotationHierarchies.isAnnotatedWith(methodBinding, Annotations.BEAN);
|
||||
if (isBeanMethod) {
|
||||
if (isCompleteAst) {
|
||||
if (context.isCompleteAst()) {
|
||||
if (currentMethod == null) {// Do not jump into anonymous class methods
|
||||
currentMethod = method;
|
||||
currentReturnTypes = new ArrayList<>();
|
||||
@@ -91,7 +90,7 @@ public class PreciseBeanTypeReconciler implements JdtAstReconciler {
|
||||
if (currentMethod == method) {
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), LABEL, method.getReturnType2().getStartPosition(), method.getReturnType2().getLength());
|
||||
if (currentReturnTypes.size() > 1) {
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
} else if (currentReturnTypes.size() == 1 && !method.resolveBinding().getReturnType().isAssignmentCompatible(currentReturnTypes.get(0))) {
|
||||
String uri = docUri.toASCIIString();
|
||||
String replacementType = currentReturnTypes.get(0).getName();
|
||||
@@ -105,7 +104,7 @@ public class PreciseBeanTypeReconciler implements JdtAstReconciler {
|
||||
new FixDescriptor(recipeId, List.of(uri), ReconcileUtils.buildLabel(LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
|
||||
currentMethod = null;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2025 Broadcom
|
||||
* 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:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
|
||||
/**
|
||||
* context information for reconcilers
|
||||
*/
|
||||
public class ReconcilingContext {
|
||||
|
||||
private final String docURI;
|
||||
|
||||
private final IProblemCollector problemCollector;
|
||||
private final boolean isCompleteAst;
|
||||
private final boolean isIndexComplete;
|
||||
|
||||
private final Set<String> dependencies; // set of fully qualified types
|
||||
private final Set<String> markedForAffectedFilesIndexing; // set of files
|
||||
|
||||
private final List<SpringIndexElement> createdIndexElements;
|
||||
|
||||
public ReconcilingContext(String docURI, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete, List<SpringIndexElement> createdIndexElements) {
|
||||
this.docURI = docURI;
|
||||
this.problemCollector = problemCollector;
|
||||
this.isCompleteAst = isCompleteAst;
|
||||
this.isIndexComplete = isIndexComplete;
|
||||
|
||||
this.dependencies = new HashSet<>();
|
||||
this.markedForAffectedFilesIndexing = new HashSet<>();
|
||||
|
||||
this.createdIndexElements = createdIndexElements;
|
||||
}
|
||||
|
||||
public String getDocURI() {
|
||||
return docURI;
|
||||
}
|
||||
|
||||
public IProblemCollector getProblemCollector() {
|
||||
return problemCollector;
|
||||
}
|
||||
|
||||
public boolean isCompleteAst() {
|
||||
return isCompleteAst;
|
||||
}
|
||||
|
||||
public boolean isIndexComplete() {
|
||||
return isIndexComplete;
|
||||
}
|
||||
|
||||
public void addDependency(String typeOfConfigClassWithImport) {
|
||||
this.dependencies.add(typeOfConfigClassWithImport);
|
||||
}
|
||||
|
||||
public Set<String> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public void markForAffetcedFilesIndexing(String file) {
|
||||
this.markedForAffectedFilesIndexing.add(file);
|
||||
}
|
||||
|
||||
public Set<String> getMarkedForAffectedFilesIndexing() {
|
||||
return markedForAffectedFilesIndexing;
|
||||
}
|
||||
|
||||
public List<SpringIndexElement> getCreatedIndexElements() {
|
||||
return createdIndexElements;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import org.openrewrite.java.spring.boot2.UnnecessarySpringExtension;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
@@ -74,7 +73,7 @@ public class UnnecessarySpringExtensionReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
return new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -95,7 +94,7 @@ public class UnnecessarySpringExtensionReconciler implements JdtAstReconciler {
|
||||
ReconcileUtils.setRewriteFixes(registry, problem, List.of(
|
||||
new FixDescriptor(UnnecessarySpringExtension.class.getName(), List.of(docUri.toASCIIString()), ReconcileUtils.buildLabel(LABEL, RecipeScope.PROJECT))
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.ide.vscode.commons.Version;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
@@ -85,7 +84,7 @@ public class WebSecurityConfigurerAdapterReconciler implements JdtAstReconciler
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(cu);
|
||||
return new ASTVisitor() {
|
||||
|
||||
@@ -111,7 +110,7 @@ public class WebSecurityConfigurerAdapterReconciler implements JdtAstReconciler
|
||||
|
||||
);
|
||||
}
|
||||
problemCollector.accept(problem);
|
||||
context.getProblemCollector().accept(problem);
|
||||
}
|
||||
return super.visit(typeDecl);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.JavaCodeActionHandler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcilingContext;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
|
||||
@@ -105,14 +106,20 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler {
|
||||
|
||||
URI uri = URI.create(doc.getUri());
|
||||
final QuickfixType rewriteFixType = quickfixRegistry.getQuickfixType(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX);
|
||||
|
||||
if (isSupported(capabilities, context) && rewriteFixType != null) {
|
||||
List<CodeAction> codeActions = cuCache.withCompilationUnit(project, uri, cu -> {
|
||||
|
||||
if (cu != null) {
|
||||
|
||||
try {
|
||||
List<CodeAction> cas = new ArrayList<>();
|
||||
List<ReconcileProblem> problems = new ArrayList<>();
|
||||
BasicProblemCollector problemsCollector = new BasicProblemCollector(problems);
|
||||
jdtReconciler.reconcile(project, uri, cu, problemsCollector, true, true);
|
||||
|
||||
ReconcilingContext reconcilingContext = new ReconcilingContext(doc.getUri(), problemsCollector, true, true, Collections.emptyList());
|
||||
jdtReconciler.reconcile(project, uri, cu, reconcilingContext);
|
||||
|
||||
for (ReconcileProblem p : problems) {
|
||||
if (p.getOffset() <= region.getOffset() && p.getOffset() + p.getLength() >= region.getOffset() + region.getLength() && severityProvider.getDiagnosticSeverity(p) == null) {
|
||||
for (QuickfixData<?> qf : p.getQuickfixes()) {
|
||||
@@ -123,6 +130,7 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler {
|
||||
}
|
||||
}
|
||||
return cas;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.springframework.ide.vscode.boot.java.SpelProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcilingContext;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
|
||||
public class JdtSpelReconciler implements JdtAstReconciler {
|
||||
@@ -44,8 +44,7 @@ public class JdtSpelReconciler implements JdtAstReconciler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu,
|
||||
IProblemCollector problemCollector, boolean isCompleteAst, boolean isIndexComplete) {
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) {
|
||||
return new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
@@ -53,7 +52,7 @@ public class JdtSpelReconciler implements JdtAstReconciler {
|
||||
.map(e -> e.getSpelRegion(node))
|
||||
.filter(o -> o.isPresent())
|
||||
.map(o -> o.get())
|
||||
.forEach(snippet -> spelReconciler.reconcile(snippet.getText(), snippet::toSingleJavaRange, problemCollector));
|
||||
.forEach(snippet -> spelReconciler.reconcile(snippet.getText(), snippet::toSingleJavaRange, context.getProblemCollector()));
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@@ -63,7 +62,7 @@ public class JdtSpelReconciler implements JdtAstReconciler {
|
||||
.map(e -> e.getSpelRegion(node))
|
||||
.filter(o -> o.isPresent())
|
||||
.map(o -> o.get())
|
||||
.forEach(snippet -> spelReconciler.reconcile(snippet.getText(), snippet::toSingleJavaRange, problemCollector));
|
||||
.forEach(snippet -> spelReconciler.reconcile(snippet.getText(), snippet::toSingleJavaRange, context.getProblemCollector()));
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.springframework.ide.vscode.boot.java.beans.CachedBean;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.CachedDiagnostics;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcilingContext;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteIndexException;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
@@ -356,8 +357,8 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
scannedFiles.add(file);
|
||||
}
|
||||
|
||||
Set<String> scannedTypes = scanFilesInternally(project, docs);
|
||||
scanAffectedFiles(project, scannedTypes, scannedFiles);
|
||||
ScanFilesInternallyResult result = scanFilesInternally(project, docs);
|
||||
scanAffectedFiles(project, result.scannedTypes, scannedFiles, result.scanResult.getMarkedForAffectedFilesIndexing());
|
||||
}
|
||||
|
||||
private void scanFile(IJavaProject project, DocumentDescriptor updatedDoc, String content) throws Exception {
|
||||
@@ -417,11 +418,11 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
|
||||
reconcileWithCompleteIndex(project, result.getMarkedForReconcilingWithCompleteIndex());
|
||||
|
||||
scanAffectedFiles(project, context.getScannedTypes(), scannedFiles);
|
||||
scanAffectedFiles(project, context.getScannedTypes(), scannedFiles, result.getMarkedForAffectedFilesIndexing());
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> scanFilesInternally(IJavaProject project, DocumentDescriptor[] docs) throws Exception {
|
||||
private ScanFilesInternallyResult scanFilesInternally(IJavaProject project, DocumentDescriptor[] docs) throws Exception {
|
||||
final boolean ignoreMethodBodies = false;
|
||||
|
||||
// this is to keep track of already scanned files to avoid endless loops due to circular dependencies
|
||||
@@ -498,14 +499,18 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
result.publishResults(symbolHandler);
|
||||
reconcileWithCompleteIndex(project, result.getMarkedForReconcilingWithCompleteIndex());
|
||||
|
||||
return scannedTypes;
|
||||
return new ScanFilesInternallyResult(scannedTypes, result);
|
||||
}
|
||||
|
||||
private static record ScanFilesInternallyResult(Set<String> scannedTypes, SpringIndexerJavaScanResult scanResult) {};
|
||||
|
||||
private void scanAffectedFiles(IJavaProject project, Set<String> changedTypes, Set<String> alreadyScannedFiles) throws Exception {
|
||||
|
||||
private void scanAffectedFiles(IJavaProject project, Set<String> changedTypes, Set<String> alreadyScannedFiles, Set<String> alreadyMarkedForAffectedFilesIndexing) throws Exception {
|
||||
log.info("Start scanning affected files for types {}", changedTypes);
|
||||
|
||||
Multimap<String, String> dependencies = dependencyTracker.getAllDependencies();
|
||||
Set<String> filesToScan = new HashSet<>();
|
||||
filesToScan.addAll(alreadyMarkedForAffectedFilesIndexing);
|
||||
|
||||
for (String file : dependencies.keys()) {
|
||||
if (!alreadyScannedFiles.contains(file)) {
|
||||
@@ -646,6 +651,9 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
boolean ignoreMethodBodies = false;
|
||||
|
||||
String[] javaFiles = markedForReconcilingWithCompleteIndex.keySet().toArray(String[]::new);
|
||||
|
||||
log.info("additional reconciling with complete index triggere for: " + Arrays.toString(javaFiles));
|
||||
|
||||
long[] modificationTimestamps = new long[javaFiles.length];
|
||||
for (int i = 0; i < javaFiles.length; i++) {
|
||||
modificationTimestamps[i] = markedForReconcilingWithCompleteIndex.get(javaFiles[i]);
|
||||
@@ -676,7 +684,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
lastModified, docRef, null, problemCollector, new ArrayList<>(), !ignoreMethodBodies, true, reconcilingResult);
|
||||
|
||||
try {
|
||||
DocumentUtils.getTempTextDocument(docURI, docRef, null);
|
||||
DocumentUtils.getTempTextDocument(docURI, docRef, null); // initialize the docRef with a real document before running validations
|
||||
reconcile(context);
|
||||
} catch (Exception e) {
|
||||
log.error("problem creating temp document during re-reconciling for: " + docURI, e);
|
||||
@@ -826,7 +834,22 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
try {
|
||||
|
||||
problemCollector.beginCollecting();
|
||||
reconciler.reconcile(context.getProject(), URI.create(context.getDocURI()), context.getCu(), problemCollector, context.isFullAst(), context.isIndexComplete());
|
||||
|
||||
List<SpringIndexElement> createdElements = context.getBeans().stream()
|
||||
.filter(cachedBean -> cachedBean.getDocURI().equals(context.getDocURI()))
|
||||
.map(cachedBean -> cachedBean.getBean())
|
||||
.toList();
|
||||
|
||||
ReconcilingContext reconcilingContext = new ReconcilingContext(context.getDocURI(), problemCollector, context.isFullAst(), context.isIndexComplete(), createdElements);
|
||||
|
||||
reconciler.reconcile(context.getProject(), URI.create(context.getDocURI()), context.getCu(), reconcilingContext);
|
||||
|
||||
for (String dependency : reconcilingContext.getDependencies()) {
|
||||
context.addDependency(dependency);
|
||||
}
|
||||
|
||||
context.getResult().markForAffectedFilesIndexing(reconcilingContext.getMarkedForAffectedFilesIndexing());
|
||||
|
||||
problemCollector.endCollecting();
|
||||
|
||||
} catch (RequiredCompleteAstException e) {
|
||||
|
||||
@@ -123,13 +123,19 @@ public class SpringIndexerJavaContext {
|
||||
|
||||
public void addDependency(ITypeBinding dependsOn) {
|
||||
if (dependsOn != null && dependsOn.isFromSource()) {
|
||||
String type = dependsOn.getKey();
|
||||
String type = dependsOn.getQualifiedName();
|
||||
|
||||
if (type != null && !scannedTypes.contains(type)) {
|
||||
dependencies.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addDependency(String qualifiedTypeName) {
|
||||
if (qualifiedTypeName != null) {
|
||||
dependencies.add(qualifiedTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getScannedTypes() {
|
||||
return scannedTypes;
|
||||
@@ -137,7 +143,7 @@ public class SpringIndexerJavaContext {
|
||||
|
||||
public void addScannedType(ITypeBinding scannedType) {
|
||||
if (scannedType != null) {
|
||||
String type = scannedType.getKey();
|
||||
String type = scannedType.getQualifiedName();
|
||||
scannedTypes.add(type);
|
||||
dependencies.remove(type);
|
||||
}
|
||||
@@ -171,5 +177,5 @@ public class SpringIndexerJavaContext {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.utils;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -37,7 +38,8 @@ import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
*/
|
||||
public class SpringIndexerJavaScanResult {
|
||||
|
||||
private final Map<String, Long> markedForReconciling;
|
||||
private final Map<String, Long> markedForReconciling; // file + modification timestamp
|
||||
private final Set<String> markedForAffetcedFilesIndexing; // file
|
||||
|
||||
private final List<CachedSymbol> generatedSymbols;
|
||||
private final List<CachedBean> generatedBeans;
|
||||
@@ -45,12 +47,15 @@ public class SpringIndexerJavaScanResult {
|
||||
|
||||
private final IJavaProject project;
|
||||
private final String[] javaFiles;
|
||||
|
||||
|
||||
public SpringIndexerJavaScanResult(IJavaProject project, String[] javaFiles) {
|
||||
this.project = project;
|
||||
this.javaFiles = javaFiles;
|
||||
|
||||
this.markedForReconciling = new HashMap<>();
|
||||
this.markedForAffetcedFilesIndexing = new HashSet<>();
|
||||
|
||||
this.generatedSymbols = new ArrayList<CachedSymbol>();
|
||||
this.generatedBeans = new ArrayList<CachedBean>();
|
||||
this.generatedDiagnostics = new ArrayList<CachedDiagnostics>();
|
||||
@@ -63,10 +68,13 @@ public class SpringIndexerJavaScanResult {
|
||||
this.javaFiles = javaFiles;
|
||||
|
||||
this.markedForReconciling = new HashMap<>();
|
||||
this.markedForAffetcedFilesIndexing = new HashSet<>();
|
||||
|
||||
this.generatedSymbols = Arrays.asList(symbols);
|
||||
this.generatedBeans = Arrays.asList(beans);
|
||||
this.generatedDiagnostics = Arrays.asList(diagnostics);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Long> getMarkedForReconcilingWithCompleteIndex() {
|
||||
return markedForReconciling;
|
||||
@@ -76,6 +84,15 @@ public class SpringIndexerJavaScanResult {
|
||||
this.markedForReconciling.put(file, lastModified);
|
||||
}
|
||||
|
||||
public void markForAffectedFilesIndexing(Collection<String> markedForAffectedFilesIndexing) {
|
||||
this.markedForAffetcedFilesIndexing.addAll(markedForAffectedFilesIndexing);
|
||||
}
|
||||
|
||||
public Set<String> getMarkedForAffectedFilesIndexing() {
|
||||
return this.markedForAffetcedFilesIndexing;
|
||||
}
|
||||
|
||||
|
||||
public List<CachedBean> getGeneratedBeans() {
|
||||
return generatedBeans;
|
||||
}
|
||||
@@ -108,5 +125,5 @@ public class SpringIndexerJavaScanResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* Copyright (c) 2023, 2025 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
|
||||
@@ -19,6 +19,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -31,6 +32,7 @@ import org.eclipse.jdt.core.dom.FileASTRequestor;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.CompositeASTVisitor;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ReconcilingContext;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava;
|
||||
import org.springframework.ide.vscode.boot.java.value.test.ValueSpelExpressionValidationTest.TestProblemCollector;
|
||||
@@ -106,7 +108,10 @@ public abstract class BaseReconcilerTest {
|
||||
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
|
||||
try {
|
||||
JdtAstReconciler reconciler = reconcilerFactory.get();
|
||||
ASTVisitor visitor = reconciler.createVisitor(project, path.toUri(), cu, problemCollector, isCompleteAst, isIndexComplete);
|
||||
String docURI = path.toUri().toASCIIString();
|
||||
|
||||
ReconcilingContext reconcilingContext = new ReconcilingContext(docURI, problemCollector, isCompleteAst, isIndexComplete, Collections.emptyList());
|
||||
ASTVisitor visitor = reconciler.createVisitor(project, path.toUri(), cu, reconcilingContext);
|
||||
|
||||
if (visitor != null) {
|
||||
// use a composite visitor here to make sure that the tests will fail if there is anything missing in the composite
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2025 Broadcom
|
||||
* 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:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Boot4JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.utils.test.TestFileScanListener;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class BeanRegistrarAdvancedReconcilingTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
@Autowired private SpringMetamodelIndex springIndex;
|
||||
|
||||
private File directory;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-framework-7-indexing/").toURI());
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoErrorOnRegisteredRegistrar() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/com/example/MyBeanRegistrar.java").toUri().toString();
|
||||
PublishDiagnosticsParams diagnosticsResult = harness.getDiagnostics(docUri);
|
||||
List<Diagnostic> diagnostics = diagnosticsResult.getDiagnostics();
|
||||
|
||||
assertEquals(0, diagnostics.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testErrorOnNonRegisteredRegistrar() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/com/example/NotRegisteredBeanRegistrar.java").toUri().toString();
|
||||
PublishDiagnosticsParams diagnosticsResult = harness.getDiagnostics(docUri);
|
||||
List<Diagnostic> diagnostics = diagnosticsResult.getDiagnostics();
|
||||
|
||||
assertEquals(1, diagnostics.size());
|
||||
assertEquals(Boot4JavaProblemType.REGISTRAR_BEAN_DECLARATION.getCode(), diagnostics.get(0).getCode().getLeft());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBeanRegistrarGetsReconciledWhenConfigChangesAddImportCase() throws Exception {
|
||||
String configDoc = directory.toPath().resolve("src/main/java/com/example/ConfigImportsBeanRegistrar.java").toUri().toString();
|
||||
String registeredRegistrarDocUri = directory.toPath().resolve("src/main/java/com/example/MyBeanRegistrar.java").toUri().toString();
|
||||
String nonRegisteredRegistrarDocUri = directory.toPath().resolve("src/main/java/com/example/NotRegisteredBeanRegistrar.java").toUri().toString();
|
||||
|
||||
// symbol pre-check
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getWorkspaceSymbolsFromSymbolIndex(registeredRegistrarDocUri);
|
||||
assertEquals(5, symbols.size());
|
||||
|
||||
// now change the config class source code and update doc
|
||||
TestFileScanListener fileScanListener = new TestFileScanListener();
|
||||
indexer.getJavaIndexer().setFileScanListener(fileScanListener);
|
||||
|
||||
String configClassSource = FileUtils.readFileToString(UriUtil.toFile(configDoc), Charset.defaultCharset());
|
||||
String updatedConfigClassSource = configClassSource.replace("@Import(MyBeanRegistrar.class)", "@Import({MyBeanRegistrar.class, NotRegisteredBeanRegistrar.class})");
|
||||
|
||||
CompletableFuture<Void> updateFuture = indexer.updateDocument(configDoc, updatedConfigClassSource, "test triggered");
|
||||
updateFuture.get(5, TimeUnit.SECONDS);
|
||||
|
||||
// check if the bean registrar files have been re-scanned
|
||||
fileScanListener.assertScannedUri(configDoc, 1);
|
||||
fileScanListener.assertScannedUri(registeredRegistrarDocUri, 1);
|
||||
fileScanListener.assertScannedUri(nonRegisteredRegistrarDocUri, 1);
|
||||
fileScanListener.assertNoFileScans(3);
|
||||
|
||||
// check diagnostics result
|
||||
PublishDiagnosticsParams diagnosticsResultForRegisteredRegistrar = harness.getDiagnostics(registeredRegistrarDocUri);
|
||||
List<Diagnostic> diagnosticsForAlreadyRegisteredRegistrar = diagnosticsResultForRegisteredRegistrar.getDiagnostics();
|
||||
assertEquals(0, diagnosticsForAlreadyRegisteredRegistrar.size());
|
||||
|
||||
PublishDiagnosticsParams diagnosticsResultForNewlyRegisteredRegistrar = harness.getDiagnostics(nonRegisteredRegistrarDocUri);
|
||||
List<Diagnostic> diagnosticsForPreviouslyNotRegisteredRegistrar = diagnosticsResultForNewlyRegisteredRegistrar.getDiagnostics();
|
||||
assertEquals(0, diagnosticsForPreviouslyNotRegisteredRegistrar.size());
|
||||
|
||||
// check if the symbols are still in place correctly
|
||||
List<? extends WorkspaceSymbol> symbolsAfterUpdate = indexer.getWorkspaceSymbolsFromSymbolIndex(registeredRegistrarDocUri);
|
||||
assertEquals(5, symbolsAfterUpdate.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBeanRegistrarGetsReconciledWhenConfigChangesRemoveImportCase() throws Exception {
|
||||
String configDoc = directory.toPath().resolve("src/main/java/com/example/ConfigImportsBeanRegistrar.java").toUri().toString();
|
||||
String registeredRegistrarDocUri = directory.toPath().resolve("src/main/java/com/example/MyBeanRegistrar.java").toUri().toString();
|
||||
|
||||
TestFileScanListener fileScanListener = new TestFileScanListener();
|
||||
indexer.getJavaIndexer().setFileScanListener(fileScanListener);
|
||||
|
||||
String configClassSource = FileUtils.readFileToString(UriUtil.toFile(configDoc), Charset.defaultCharset());
|
||||
String updatedConfigClassSource = configClassSource.replace("@Import(MyBeanRegistrar.class)", "");
|
||||
|
||||
CompletableFuture<Void> updateFuture = indexer.updateDocument(configDoc, updatedConfigClassSource, "test triggered");
|
||||
updateFuture.get(5, TimeUnit.SECONDS);
|
||||
|
||||
fileScanListener.assertScannedUri(configDoc, 1);
|
||||
fileScanListener.assertScannedUri(registeredRegistrarDocUri, 1);
|
||||
fileScanListener.assertNoFileScans(2);
|
||||
|
||||
PublishDiagnosticsParams diagnosticsResultForPreviouslyRegisteredRegistrar = harness.getDiagnostics(registeredRegistrarDocUri);
|
||||
List<Diagnostic> diagnosticsForPreviouslyRegisteredRegistrar = diagnosticsResultForPreviouslyRegisteredRegistrar.getDiagnostics();
|
||||
assertEquals(1, diagnosticsForPreviouslyRegisteredRegistrar.size());
|
||||
assertEquals(Boot4JavaProblemType.REGISTRAR_BEAN_DECLARATION.getCode(), diagnosticsForPreviouslyRegisteredRegistrar.get(0).getCode().getLeft());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
|
||||
public class BeanRegistrarDeclarationReconcilerTest extends BaseReconcilerTest {
|
||||
|
||||
|
||||
@Override
|
||||
protected String getFolder() {
|
||||
return "sf7validation";
|
||||
|
||||
@@ -109,8 +109,9 @@ public class RequestMappingSymbolProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
//TODO: Enable when JDT Core 3.41 or higher is adopted. See: https://github.com/eclipse-jdt/eclipse.jdt.core/pull/3416
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
// TODO: Enable when JDT Core 3.41 or higher is adopted. See: https://github.com/eclipse-jdt/eclipse.jdt.core/pull/3416
|
||||
// additional comment: switched the string to not contain the "key" of the AST node, but the fully qualified type name, therefore enabling this again everywhere
|
||||
// @DisabledOnOs(OS.WINDOWS)
|
||||
void testSimpleRequestMappingSymbolFromConstantInDifferentClass() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClassWithConstantInDifferentClass.java").toUri().toString();
|
||||
String constantsUri = directory.toPath().resolve("src/main/java/org/test/Constants.java").toUri().toString();
|
||||
@@ -120,7 +121,7 @@ public class RequestMappingSymbolProviderTest {
|
||||
|
||||
//Verify whether dependency tracker logics works properly for this example.
|
||||
SpringIndexerJavaDependencyTracker dt = indexer.getJavaIndexer().getDependencyTracker();
|
||||
assertEquals(ImmutableSet.of("Lorg/test/Constants;"), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
assertEquals(ImmutableSet.of("org.test.Constants"), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
|
||||
TestFileScanListener fileScanListener = new TestFileScanListener();
|
||||
indexer.getJavaIndexer().setFileScanListener(fileScanListener);
|
||||
@@ -135,7 +136,8 @@ public class RequestMappingSymbolProviderTest {
|
||||
|
||||
@Test
|
||||
//TODO: Enable when JDT Core 3.41 or higher is adopted. See: https://github.com/eclipse-jdt/eclipse.jdt.core/pull/3416
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
// additional comment: switched the string to not contain the "key" of the AST node, but the fully qualified type name, therefore enabling this again everywhere
|
||||
// @DisabledOnOs(OS.WINDOWS)
|
||||
void testUpdateDocumentWithConstantFromDifferentClass() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClassWithConstantInDifferentClass.java").toUri().toString();
|
||||
String constantsUri = directory.toPath().resolve("src/main/java/org/test/Constants.java").toUri().toString();
|
||||
@@ -145,7 +147,7 @@ public class RequestMappingSymbolProviderTest {
|
||||
|
||||
//Verify whether dependency tracker logics works properly for this example.
|
||||
SpringIndexerJavaDependencyTracker dt = indexer.getJavaIndexer().getDependencyTracker();
|
||||
assertEquals(ImmutableSet.of("Lorg/test/Constants;"), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
assertEquals(ImmutableSet.of("org.test.Constants"), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
|
||||
TestFileScanListener fileScanListener = new TestFileScanListener();
|
||||
indexer.getJavaIndexer().setFileScanListener(fileScanListener);
|
||||
@@ -153,7 +155,7 @@ public class RequestMappingSymbolProviderTest {
|
||||
CompletableFuture<Void> updateFuture = indexer.updateDocument(docUri, FileUtils.readFileToString(UriUtil.toFile(docUri), Charset.defaultCharset()), "test triggered");
|
||||
updateFuture.get(5, TimeUnit.SECONDS);
|
||||
|
||||
assertEquals(ImmutableSet.of("Lorg/test/Constants;"), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
assertEquals(ImmutableSet.of("org.test.Constants"), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
|
||||
fileScanListener.assertScannedUris(docUri);
|
||||
fileScanListener.assertScannedUri(constantsUri, 0);
|
||||
|
||||
@@ -65,4 +65,8 @@ public class TestFileScanListener implements FileScanListener {
|
||||
public void reset() {
|
||||
scannedFiles.clear();
|
||||
}
|
||||
|
||||
public void assertNoFileScans(int count) {
|
||||
assertEquals(count, scannedFiles.size());
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import org.springframework.beans.factory.BeanRegistrar;
|
||||
import org.springframework.beans.factory.BeanRegistry;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class NotRegisterredBeanRegistrar implements BeanRegistrar {
|
||||
public class NotRegisteredBeanRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
Reference in New Issue
Block a user