diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java index aede53c43..1b5518415 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java @@ -20,9 +20,12 @@ import java.util.stream.Stream; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.utils.CuDeclarationUtils; import org.springframework.ide.vscode.commons.util.CollectorUtil; import com.google.common.collect.ImmutableList; @@ -74,6 +77,32 @@ public abstract class AnnotationHierarchies { } } + public static Collection getDirectSuperAnnotations2(TypeBinding typeBinding) { + synchronized(lock) { + try { + AnnotationBinding[] annotations = typeBinding.getAnnotations(); + if (annotations != null && annotations.length != 0) { + ImmutableList.Builder superAnnotations = ImmutableList.builder(); + for (AnnotationBinding ab : annotations) { + TypeBinding sa = ab.getAnnotationType(); + if (sa != null) { + if (!ignoreAnnotation(CuDeclarationUtils.getQualifiedName(sa))) { + superAnnotations.add(sa); + } + } + } + return superAnnotations.build(); + } + } + catch (AbortCompilation e) { + log.debug("compilation aborted ", e); + // ignore this, it is most likely caused by broken source code, a broken classpath, or some optional dependencies not being on the classpath + } + + return ImmutableList.of(); + } + } + public static Set getTransitiveSuperAnnotations(ITypeBinding typeBinding) { synchronized(lock) { Set seen = new HashSet<>(); @@ -81,6 +110,15 @@ public abstract class AnnotationHierarchies { return seen; } } + + public static Set getTransitiveSuperAnnotations2(TypeBinding typeBinding) { + synchronized(lock) { + Set seen = new HashSet<>(); + findTransitiveSupers2(typeBinding, seen).collect(Collectors.toList()); + return seen; + } + } + public static Stream findTransitiveSupers(ITypeBinding typeBinding, Set seen) { synchronized(lock) { @@ -97,6 +135,21 @@ public abstract class AnnotationHierarchies { } } + public static Stream findTransitiveSupers2(TypeBinding typeBinding, Set seen) { + synchronized(lock) { + String qname = CuDeclarationUtils.getQualifiedName(typeBinding); + if (seen.add(qname)) { + return Stream.concat( + Stream.of(typeBinding), + getDirectSuperAnnotations2(typeBinding).stream().flatMap(superBinding -> + findTransitiveSupers2(superBinding, seen) + ) + ); + } + return Stream.empty(); + } + } + public static boolean isSubtypeOf(Annotation annotation, String fqAnnotationTypeName) { synchronized(lock) { ITypeBinding annotationType = annotation.resolveTypeBinding(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index dc5a9c001..40ef9c3ce 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -27,6 +27,9 @@ import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.HoverParams; @@ -99,6 +102,35 @@ public class BootJavaHoverProvider implements HoverHandler { if (processLiveData.length == 0) return new CodeLens[0]; if (project == null) return new CodeLens[0]; + + server.getCompilationUnitCache().withCompilationUnitDeclaration(project, URI.create(document.getUri()), cu -> { + Collection result = new LinkedHashSet<>(); + cu.traverse(new org.eclipse.jdt.internal.compiler.ASTVisitor() { + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration localTypeDeclaration, + BlockScope scope) { + extractLiveHintsForType2(localTypeDeclaration, document, project, processLiveData, result); + return super.visit(localTypeDeclaration, scope); + } + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration memberTypeDeclaration, + ClassScope scope) { + extractLiveHintsForType2(memberTypeDeclaration, document, project, processLiveData, result); + return super.visit(memberTypeDeclaration, scope); + } + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, + CompilationUnitScope scope) { + extractLiveHintsForType2(typeDeclaration, document, project, processLiveData, result); + return super.visit(typeDeclaration, scope); + } + + }, (BlockScope) null); + return result; + }); return server.getCompilationUnitCache().withCompilationUnit(project, URI.create(document.getUri()), cu -> { Collection result = new LinkedHashSet<>(); @@ -193,6 +225,17 @@ public class BootJavaHoverProvider implements HoverHandler { } } + protected void extractLiveHintsForType2(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, TextDocument doc, IJavaProject project, + SpringProcessLiveData[] processLiveData, Collection result) { + Collection providers = this.hoverProviders.getAll(); + for (HoverProvider provider : providers) { + Collection hints = provider.getLiveHintCodeLenses2(project, typeDeclaration, doc, processLiveData); + if (hints!=null) { + result.addAll(hints); + } + } + } + protected void extractLiveHintsForAnnotation(Annotation annotation, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData, Collection result) { ITypeBinding type = annotation.resolveTypeBinding(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java index 2af069787..68dcfa80c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java @@ -56,5 +56,9 @@ public interface HoverProvider { default Collection getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) { return null; } + + default Collection getLiveHintCodeLenses2(IJavaProject project, org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) { + return null; + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java index d8f70a8e3..a2117dfd2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java @@ -103,6 +103,59 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider return null; } + protected List assembleCodeLenses2(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider, + TextDocument doc, Range range, org.eclipse.jdt.internal.compiler.ast.ASTNode node) { + List codeLenses = null; + boolean beanFound = false; + for (SpringProcessLiveData liveData : processLiveData) { + + LiveBean definedBean = definedBeanProvider.definedBean(liveData); + if (definedBean == null) { + continue; + } + + beanFound = true; + + List relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean); + + if (!relevantBeans.isEmpty()) { + List injectedBeans = getRelevantInjectedIntoBeans(project, liveData, definedBean, relevantBeans); + ImmutableList.Builder builder = ImmutableList.builder(); + if (!injectedBeans.isEmpty()) { + // Break out of the loop. Just look for the first app with injected into beans + List injectedCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, injectedBeans, + BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR); + builder.addAll( + injectedCodeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses); + } + + // Wired beans code lenses + List wiredBeans = findWiredBeans2(project, liveData, relevantBeans, node); + builder.addAll(assembleCodeLenseForAutowired2(wiredBeans, project, liveData, doc, range, node)); + + // Startup metrics CodeLens + if (liveData.getStartupMetrics() != null) { + Duration startupTime = liveData.getStartupMetrics().getBeanInstanciationTime(definedBean.getId()); + if (startupTime != null) { + builder.add(LiveHoverUtils.createCodeLenseForBeanStartupMetric(range, startupTime)); + } + } + + // If Injected into and Wired beans are found for an app just return ocde lenses for the bean from the app + codeLenses = builder.build(); + break; + } else if (liveData.getStartupMetrics() != null && liveData.getStartupMetrics().getBeanInstanciationTime(definedBean.getId()) != null) { + Duration startupTime = liveData.getStartupMetrics().getBeanInstanciationTime(definedBean.getId()); + codeLenses = ImmutableList.of(LiveHoverUtils.createCodeLenseForBeanStartupMetric(range, startupTime)); + } + + } + if (beanFound) { + return codeLenses == null || codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : codeLenses; + } + return null; + } + protected List assembleCodeLenses(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider, TextDocument doc, Range range, ASTNode node) { List codeLenses = null; @@ -162,10 +215,20 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider INLINE_BEANS_STRING_SEPARATOR); } + protected List assembleCodeLenseForAutowired2(List wiredBeans, IJavaProject project, SpringProcessLiveData processLiveData, TextDocument doc, Range nameRange, org.eclipse.jdt.internal.compiler.ast.ASTNode astNode) { + return LiveHoverUtils.createCodeLensesForBeans(nameRange, wiredBeans, + AutowiredHoverProvider.BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, + INLINE_BEANS_STRING_SEPARATOR); + } + protected List findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List relevantBeans, ASTNode astNode) { return Collections.emptyList(); } + protected List findWiredBeans2(IJavaProject project, SpringProcessLiveData liveData, List relevantBeans, org.eclipse.jdt.internal.compiler.ast.ASTNode astNode) { + return Collections.emptyList(); + } + protected Hover assembleHover(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider, ASTNode astNode, boolean injected, boolean wired) { StringBuilder hover = new StringBuilder(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java index c531304b2..bed975382 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java @@ -98,6 +98,22 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv return Collections.emptyList(); } + //TODO: implement me + @Override + protected List findWiredBeans2(IJavaProject project, SpringProcessLiveData liveData, List relevantBeans, org.eclipse.jdt.internal.compiler.ast.ASTNode astNode) { +// if (astNode instanceof org.eclipse.jdt.internal.compiler.ast.Annotation) { +// // @Bean annotation case +// MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((org.eclipse.jdt.internal.compiler.ast.Annotation) astNode); +// if (beanMethod != null) { +// return AutowiredHoverProvider.getRelevantAutowiredBeans(project, beanMethod, liveData, relevantBeans); +// } +// } else if (astNode instanceof SingleVariableDeclaration) { +// // Bean method parameter case +// return AutowiredHoverProvider.getRelevantAutowiredBeans(project, astNode, liveData, relevantBeans); +// } + return Collections.emptyList(); + } + @Override protected List assembleCodeLenseForAutowired(List wiredBeans, IJavaProject project, SpringProcessLiveData liveData, TextDocument doc, Range nameRange, ASTNode astNode) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index e179d7961..3f734d2a1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -22,6 +22,7 @@ import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Range; @@ -38,6 +39,7 @@ import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.util.text.DocumentRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; @@ -97,10 +99,28 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP }); } + private static String getBeanId2(Annotation annotation, TypeBinding beanType, boolean isStatic) { + return ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString).orElseGet(() -> { + if (beanType.enclosingType() == null) { + return BeanUtils.getBeanNameFromType(new String(beanType.shortReadableName())); + } else { + if (isStatic) { + return BeanUtils.getBeanNameFromType(new String(beanType.qualifiedSourceName())); + } else { + return getBeanType2(beanType).toString(); + } + } + }); + } + private static String getBeanType(ITypeBinding beanType) { return beanType.getBinaryName(); } + private static String getBeanType2(TypeBinding beanType) { + return new String(beanType.qualifiedSourceName()).replace('.', '$'); + } + @Override public Collection getLiveHintCodeLenses(IJavaProject project, TypeDeclaration typeDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) { @@ -165,6 +185,33 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP } return null; } + + + + @Override + public Collection getLiveHintCodeLenses2(IJavaProject project, + org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, TextDocument doc, + SpringProcessLiveData[] processLiveData) { + if (processLiveData.length > 0 && !isComponentAnnotatedType2(typeDeclaration)) { + try { + TypeBinding beanType = typeDeclaration.binding; + if (beanType != null) { + String id = getBeanId2(null, beanType, Flags.isStatic(typeDeclaration.modifiers)); + Optional nameRange = Optional.of(new DocumentRegion(doc, typeDeclaration.sourceStart, typeDeclaration.sourceEnd).asRange()); + if (nameRange.isPresent()) { + List codeLenses = assembleCodeLenses2(project, processLiveData, liveData -> definedBean(liveData, getBeanType2(beanType), id), doc, + nameRange.get(), typeDeclaration); + if (codeLenses != null) { + return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; + } + } + } + } catch (Exception e) { + LOG.error("", e); + } + } + return ImmutableList.of(); + } @Override protected List findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List relevantBeans, @@ -178,6 +225,15 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP return typeDeclaration == null ? Collections.emptyList() : LiveHoverUtils.findAllDependencyBeans(liveData, relevantBeans); } + private boolean isComponentAnnotatedType2(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration) { + for (org.eclipse.jdt.internal.compiler.ast.Annotation a : typeDeclaration.annotations) { + if (a.type != null && a.type.resolvedType != null && isComponentAnnotation2(a.type.resolvedType)) { + return true; + } + } + return false; + } + private boolean isComponentAnnotatedType(TypeDeclaration typeDeclaration) { List modifiers = typeDeclaration.modifiers(); for (Object modifier : modifiers) { @@ -201,5 +257,17 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP return false; } + + private boolean isComponentAnnotation2(TypeBinding type) { + Set transitiveSuperAnnotations = AnnotationHierarchies.getTransitiveSuperAnnotations2(type); + for (String annotationType : transitiveSuperAnnotations) { + if (Annotations.COMPONENT.equals(annotationType)) { + return true; + } + } + + return false; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CuDeclarationUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CuDeclarationUtils.java index 8b0c19b8c..e76239bcc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CuDeclarationUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CuDeclarationUtils.java @@ -2,7 +2,11 @@ package org.springframework.ide.vscode.boot.java.utils; import java.util.Arrays; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; public class CuDeclarationUtils { @@ -20,5 +24,23 @@ public class CuDeclarationUtils { // line start from 1 hence -1 and -1 for getting end of previous line return line == 0 ? offset : offset - cu.compilationResult.lineSeparatorPositions[line - 2] - 1; // -1 at end because offset is for the line separator on the previous line } + + public static String getQualifiedName(TypeBinding type) { + StringBuilder sb = new StringBuilder(); + sb.append(type.qualifiedPackageName()); + sb.append('.'); + sb.append(type.qualifiedSourceName()); + return sb.toString(); + } + + + public static MethodDeclaration getAnnotatedMethod(Annotation annotation) { +// ASTNode parent = annotation.recipient.; +// if (parent instanceof MethodDeclaration) { +// return (MethodDeclaration)parent; +// } + return null; + } + } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java index 5fa5018c7..23bcc0cda 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils.test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,17 +30,24 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.CuDeclarationUtils; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.project.harness.ProjectsHarness; @@ -159,4 +167,80 @@ public class AstParserTest { }, new CompilationUnitScope(cu, new CompilerOptions(CompilationUnitCache.createCompilerOptions())/*new LookupEnvironment(null, new CompilerOptions(CompilationUnitCache.createCompilerOptions()), null, envTuple.getT2())*/)); } + @Test + void testCuDeclarationAnnotations() throws Exception { + URL sourceUrl = SourceLinks.source(jp, "org.springframework.boot.autoconfigure.SpringBootApplication").get(); + + URI uri = sourceUrl.toURI(); + + String unitName = "SpringBootApplication"; + + char[] content = IOUtils.toString(uri).toCharArray(); + + Tuple2, INameEnvironmentWithProgress> envTuple = CompilationUnitCache.createLookupEnvTuple(jp); + + + CompilationUnitDeclaration cu = CompilationUnitCache.parse3(content, uri.toASCIIString(), unitName, envTuple.getT1(), envTuple.getT2()); + + assertNotNull(cu); + + cu.traverse(new org.eclipse.jdt.internal.compiler.ASTVisitor() { + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation annotation, BlockScope scope) { + processAnnotation(annotation); + return super.visit(annotation, scope); + } + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation annotation, ClassScope scope) { + processAnnotation(annotation); + return super.visit(annotation, scope); + } + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation annotation, + BlockScope scope) { + processAnnotation(annotation); + return super.visit(annotation, scope); + } + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation annotation, + ClassScope scope) { + processAnnotation(annotation); + return super.visit(annotation, scope); + } + + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.NormalAnnotation annotation, BlockScope scope) { + processAnnotation(annotation); + return super.visit(annotation, scope); + } + + @Override + public boolean visit(org.eclipse.jdt.internal.compiler.ast.NormalAnnotation annotation, ClassScope scope) { + processAnnotation(annotation); + return super.visit(annotation, scope); + } + + private void processAnnotation(Annotation a) { + if (a.recipient == null) { + System.out.println(CuDeclarationUtils.getQualifiedName(a.resolvedType)); + } else { + if (a.recipient instanceof MethodBinding) { + MethodBinding mb = (MethodBinding) a.recipient; + assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", CuDeclarationUtils.getQualifiedName(mb.declaringClass)); + } else if (a.recipient instanceof TypeBinding) { + assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", CuDeclarationUtils.getQualifiedName((TypeBinding)a.recipient)); + } else if (a.recipient instanceof FieldBinding) { + FieldBinding fb = (FieldBinding) a.recipient; + assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", CuDeclarationUtils.getQualifiedName(fb.declaringClass)); + } + } + } + + }, new CompilationUnitScope(cu, new CompilerOptions(CompilationUnitCache.createCompilerOptions())/*new LookupEnvironment(null, new CompilerOptions(CompilationUnitCache.createCompilerOptions()), null, envTuple.getT2())*/)); + } }