Cleanup `AnnotationHierarchies#hasTransitiveSuperAnnotationType(..)

This commit is contained in:
aboyko
2024-06-13 13:22:28 -04:00
parent 3c4998266d
commit 0312d53468
4 changed files with 852 additions and 3 deletions

View File

@@ -139,7 +139,7 @@ public class BootJavaCompletionEngineConfigurer {
if (annotations != null) {
for (int i = 0; i < annotations.length; i++) {
ITypeBinding annotationType = annotations[i].getAnnotationType();
if (AnnotationHierarchies.isMetaAnnotation(annotationType, (name) -> requiredAnnotation.equals(name))) {
if (AnnotationHierarchies.hasTransitiveSuperAnnotationType(annotationType, requiredAnnotation)) {
return true;
}
}

View File

@@ -12,6 +12,9 @@ package org.springframework.ide.vscode.boot.java.annotations;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -78,9 +81,49 @@ public abstract class AnnotationHierarchies {
public static boolean hasTransitiveSuperAnnotationType(ITypeBinding typeBinding, String annotationType) {
synchronized(lock) {
return isMetaAnnotation(typeBinding, annotationType::equals);
if (typeBinding != null && annotationType != null) {
for (Iterator<ITypeBinding> itr = metaHierarchy(typeBinding); itr.hasNext();) {
ITypeBinding t = itr.next();
if (annotationType.equals(t.getQualifiedName())) {
return true;
}
}
}
return false;
}
}
public static Iterator<ITypeBinding> metaHierarchy(ITypeBinding actualAnnotation) {
return new Iterator<>() {
private HashSet<String> seen = new HashSet<>();
private Queue<ITypeBinding> queue = new LinkedList<>();
{
seen.add(actualAnnotation.getQualifiedName());
queue.add(actualAnnotation);
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
@Override
public ITypeBinding next() {
ITypeBinding next = queue.poll();
for (ITypeBinding a : getDirectSuperAnnotations(next)) {
String qName = a.getQualifiedName();
if (!seen.contains(qName)) {
seen.add(qName);
queue.add(a);
}
}
return next;
}
};
}
public static Collection<ITypeBinding> getMetaAnnotations(ITypeBinding actualAnnotation, Predicate<String> isKeyAnnotationName) {
synchronized(lock) {

View File

@@ -111,7 +111,7 @@ public class AnnotationHierarchiesTests {
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "test.CustomComponent2")).isTrue();
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.context.annotation.Configuration")).isFalse();
assertThat(AnnotationHierarchies.isMetaAnnotation(binding, "test.CustomComponent2"::equals)).isTrue();
assertThat(AnnotationHierarchies.getMetaAnnotations(binding, qn -> true).stream().toList().size()).isEqualTo(3);
assertThat(AnnotationHierarchies.isMetaAnnotation(binding, "org.springframework.context.annotation.Configuration"::equals)).isFalse();
assertThat(AnnotationHierarchies.getDirectSuperAnnotations(binding).stream().toList().size()).isEqualTo(2);
@@ -129,4 +129,56 @@ public class AnnotationHierarchiesTests {
}, null);
}
@Test
void simpleHierarchy() throws Exception {
String projectName = "test-spring-validations";
IJavaProject project = ProjectsHarness.INSTANCE.mavenProject(projectName);
Path file = createFile(projectName, "test", "MyComponent.java", """
package test;
import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
public class MyComponent {
}
""");
SpringIndexerJava.createParser(project, true).createASTs(new String[] { file.toFile().toString() }, null, new String[0], new FileASTRequestor() {
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
cu.accept(new ASTVisitor() {
@Override
public boolean visit(MarkerAnnotation node) {
ITypeBinding binding = node.resolveTypeBinding();
assertThat(binding).isNotNull();
assertThat(binding.getQualifiedName()).isEqualTo("org.springframework.boot.autoconfigure.SpringBootApplication");
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "test.CustomComponent2")).isFalse();
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.context.annotation.Configuration")).isTrue();
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.boot.autoconfigure.SpringBootApplication")).isTrue();
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.stereotype.Component")).isTrue();
List<ITypeBinding> metaAnnotations = AnnotationHierarchies.getMetaAnnotations(binding, qn -> true).stream().toList();
assertThat(metaAnnotations.size()).isEqualTo(8);
assertThat(AnnotationHierarchies.isMetaAnnotation(binding, "org.springframework.context.annotation.Configuration"::equals)).isTrue();
assertThat(AnnotationHierarchies.getDirectSuperAnnotations(binding).stream().toList().size()).isEqualTo(3);
IAnnotationBinding annotationBinding = node.resolveAnnotationBinding();
assertThat(annotationBinding).isNotNull();
assertThat(AnnotationHierarchies.getDirectSuperAnnotationBindings(annotationBinding).stream().toList().size()).isEqualTo(3);
assertThat(AnnotationHierarchies.isSubtypeOf(node, "test.CustomComponent2")).isFalse();
assertThat(AnnotationHierarchies.isSubtypeOf(node, "org.springframework.context.annotation.Configuration")).isTrue();
assertThat(AnnotationHierarchies.isSubtypeOf(node, "org.springframework.boot.autoconfigure.SpringBootApplication")).isTrue();
assertThat(AnnotationHierarchies.isSubtypeOf(node, "org.springframework.stereotype.Component")).isTrue();
return super.visit(node);
}
});
}
}, null);
}
}