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 a7f59aa50..65154df7d 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 @@ -45,7 +45,6 @@ import org.springframework.ide.vscode.boot.java.events.EventListenerIndexer; import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.reconcilers.NotRegisteredBeansReconciler; -import org.springframework.ide.vscode.boot.java.reconcilers.ReconcileUtils; import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException; import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingIndexer; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; @@ -364,7 +363,7 @@ public class ComponentSymbolProvider implements SymbolProvider { ITypeBinding typeBinding = typeDeclaration.resolveBinding(); if (typeBinding == null) return; - if (ReconcileUtils.implementsAnyType(NotRegisteredBeansReconciler.AOT_BEANS, typeBinding)) { + if (ASTUtils.isAnyTypeInHierarchy(typeBinding, NotRegisteredBeansReconciler.AOT_BEANS)) { String type = typeBinding.getQualifiedName(); String docUri = context.getDocURI(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanPostProcessingIgnoreInAotReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanPostProcessingIgnoreInAotReconciler.java index f976dd907..865f3a4f6 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanPostProcessingIgnoreInAotReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanPostProcessingIgnoreInAotReconciler.java @@ -14,6 +14,7 @@ import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.spri import java.net.URI; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jdt.core.dom.ASTVisitor; @@ -23,6 +24,7 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType; +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.ProblemType; @@ -108,7 +110,7 @@ public class BeanPostProcessingIgnoreInAotReconciler implements JdtAstReconciler } private static boolean isApplicable(ITypeBinding type) { - return ReconcileUtils.implementsType(RUNTIME_BEAN_POST_PROCESSOR, type) && ReconcileUtils.implementsType(COMPILE_BEAN_POST_PROCESSOR, type); + return ASTUtils.areAllTypesInHierarchy(type, Set.of(RUNTIME_BEAN_POST_PROCESSOR, COMPILE_BEAN_POST_PROCESSOR)); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/NotRegisteredBeansReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/NotRegisteredBeansReconciler.java index 46fbe7bfe..379435ce0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/NotRegisteredBeansReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/NotRegisteredBeansReconciler.java @@ -30,6 +30,7 @@ import org.eclipse.jdt.core.dom.TypeDeclaration; import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType; +import org.springframework.ide.vscode.boot.java.utils.ASTUtils; 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; @@ -83,7 +84,7 @@ public class NotRegisteredBeansReconciler implements JdtAstReconciler { if (!node.isInterface() && !Modifier.isAbstract(node.getModifiers())) { ITypeBinding type = node.resolveBinding(); - if (type != null && ReconcileUtils.implementsAnyType(AOT_BEANS, type)) { + if (type != null && ASTUtils.isAnyTypeInHierarchy(type, AOT_BEANS)) { // // reconcile AOT Proceesor itself diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcileUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcileUtils.java index 7066bec23..273b043f9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcileUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcileUtils.java @@ -148,34 +148,6 @@ public class ReconcileUtils { return typeUsed.get(); } - public static boolean implementsType(String fqName, ITypeBinding type) { - if (fqName.equals(type.getQualifiedName())) { - return true; - } else { - for (ITypeBinding t : type.getInterfaces()) { - if (implementsType(fqName, t)) { - return true; - } - } - } - return false; - } - - public static boolean implementsAnyType(Collection fqNames, ITypeBinding type) { - if (fqNames.contains(type.getQualifiedName())) { - return true; - } else { - for (ITypeBinding t : type.getInterfaces()) { - if (implementsAnyType(fqNames, t)) { - return true; - } - } - } - return false; - } - - - public static String getSimpleName(String fqName) { int idx = fqName.lastIndexOf('.'); if (idx >= 0 && idx < fqName.length() - 1) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java index 5f4c20ee5..bf123b23b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java @@ -10,13 +10,18 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; +import java.util.Queue; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Stream; @@ -394,36 +399,14 @@ public class ASTUtils { } public static ITypeBinding findInTypeHierarchy(ITypeBinding resolvedType, Set typesToCheck) { - ITypeBinding[] interfaces = resolvedType.getInterfaces(); - - for (ITypeBinding resolvedInterface : interfaces) { - String simplifiedType = null; - - if (resolvedInterface.isParameterizedType()) { - simplifiedType = resolvedInterface.getBinaryName(); - } - else { - simplifiedType = resolvedInterface.getQualifiedName(); - } - - if (typesToCheck.contains(simplifiedType)) { - return resolvedInterface; - } - else { - ITypeBinding result = findInTypeHierarchy(resolvedInterface, typesToCheck); - if (result != null) { - return result; - } + for (Iterator itr = getSuperTypesIterator(resolvedType); itr.hasNext();) { + ITypeBinding b = itr.next(); + String fqn = b.isParameterizedType() ? b.getBinaryName() : b.getQualifiedName(); + if (typesToCheck.contains(fqn)) { + return b; } } - - ITypeBinding superclass = resolvedType.getSuperclass(); - if (superclass != null) { - return findInTypeHierarchy(superclass, typesToCheck); - } - else { - return null; - } + return null; } public static Optional getImportsEdit(CompilationUnit cu, Collection imprts, IDocument doc) { @@ -463,40 +446,75 @@ public class ASTUtils { // } // public static void findSupertypes(ITypeBinding binding, Set supertypesCollector) { - - // interfaces - ITypeBinding[] interfaces = binding.getInterfaces(); - for (ITypeBinding resolvedInterface : interfaces) { - String simplifiedType = null; - if (resolvedInterface.isParameterizedType()) { - simplifiedType = resolvedInterface.getBinaryName(); - } - else { - simplifiedType = resolvedInterface.getQualifiedName(); - } - - if (simplifiedType != null) { - supertypesCollector.add(simplifiedType); - findSupertypes(resolvedInterface, supertypesCollector); + for (Iterator itr = getSuperTypesFqNamesIterator(binding); itr.hasNext();) { + supertypesCollector.add(itr.next()); + } + } + + public static boolean isAnyTypeInHierarchy(ITypeBinding binding, Collection typeFqns) { + for (Iterator itr = getSuperTypesFqNamesIterator(binding); itr.hasNext();) { + String fqn = itr.next(); + if (typeFqns.contains(fqn)) { + return true; } } - - // superclasses - ITypeBinding superclass = binding.getSuperclass(); - if (superclass != null) { - String simplifiedType = null; - if (superclass.isParameterizedType()) { - simplifiedType = superclass.getBinaryName(); + return false; + } + + public static boolean areAllTypesInHierarchy(ITypeBinding binding, Collection typeFqns) { + HashSet notFound = new HashSet<>(typeFqns); + for (Iterator itr = getSuperTypesFqNamesIterator(binding); itr.hasNext() && !notFound.isEmpty();) { + notFound.remove(itr.next()); + } + return notFound.isEmpty(); + } + + public static Iterator getSuperTypesIterator(ITypeBinding binding) { + final Queue q = new ArrayDeque<>(10); + q.add(binding); + return new Iterator() { + + @Override + public boolean hasNext() { + return !q.isEmpty(); } - else { - simplifiedType = superclass.getQualifiedName(); + + @Override + public ITypeBinding next() { + ITypeBinding t = q.poll(); + if (t == null) { + throw new NoSuchElementException(); + } + for (ITypeBinding b : t.getInterfaces()) { + if (b != null) { + q.add(b); + } + } + if (t.getSuperclass() != null) { + q.add(t.getSuperclass()); + } + return t; } - if (simplifiedType != null) { - supertypesCollector.add(simplifiedType); - findSupertypes(superclass, supertypesCollector); + }; + } + + public static Iterator getSuperTypesFqNamesIterator(ITypeBinding binding) { + Iterator itr = getSuperTypesIterator(binding); + return new Iterator() { + + @Override + public boolean hasNext() { + return itr.hasNext(); } - } + + @Override + public String next() { + ITypeBinding b = itr.next(); + return b.isParameterizedType() ? b.getBinaryName() : b.getQualifiedName(); + } + + }; } public static InjectionPoint[] findInjectionPoints(MethodDeclaration method, TextDocument doc) throws BadLocationException {