Skip to content

Commit 8b47485

Browse files
committed
Cleanup `AnnotationHierarchies#hasTransitiveSuperAnnotationType(..)
1 parent a28764a commit 8b47485

File tree

4 files changed

+852
-3
lines changed

4 files changed

+852
-3
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public boolean appliesTo(ASTNode node, int offset, CharSequence prefix) {
139139
if (annotations != null) {
140140
for (int i = 0; i < annotations.length; i++) {
141141
ITypeBinding annotationType = annotations[i].getAnnotationType();
142-
if (AnnotationHierarchies.isMetaAnnotation(annotationType, (name) -> requiredAnnotation.equals(name))) {
142+
if (AnnotationHierarchies.hasTransitiveSuperAnnotationType(annotationType, requiredAnnotation)) {
143143
return true;
144144
}
145145
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
import java.util.Collection;
1414
import java.util.HashSet;
15+
import java.util.Iterator;
16+
import java.util.LinkedList;
17+
import java.util.Queue;
1518
import java.util.Set;
1619
import java.util.function.Predicate;
1720
import java.util.stream.Stream;
@@ -78,9 +81,49 @@ public static boolean isSubtypeOf(Annotation annotation, String fqAnnotationType
7881

7982
public static boolean hasTransitiveSuperAnnotationType(ITypeBinding typeBinding, String annotationType) {
8083
synchronized(lock) {
81-
return isMetaAnnotation(typeBinding, annotationType::equals);
84+
if (typeBinding != null && annotationType != null) {
85+
for (Iterator<ITypeBinding> itr = metaHierarchy(typeBinding); itr.hasNext();) {
86+
ITypeBinding t = itr.next();
87+
if (annotationType.equals(t.getQualifiedName())) {
88+
return true;
89+
}
90+
}
91+
}
92+
return false;
8293
}
8394
}
95+
96+
public static Iterator<ITypeBinding> metaHierarchy(ITypeBinding actualAnnotation) {
97+
return new Iterator<>() {
98+
99+
private HashSet<String> seen = new HashSet<>();
100+
private Queue<ITypeBinding> queue = new LinkedList<>();
101+
102+
{
103+
seen.add(actualAnnotation.getQualifiedName());
104+
queue.add(actualAnnotation);
105+
}
106+
107+
@Override
108+
public boolean hasNext() {
109+
return !queue.isEmpty();
110+
}
111+
112+
@Override
113+
public ITypeBinding next() {
114+
ITypeBinding next = queue.poll();
115+
for (ITypeBinding a : getDirectSuperAnnotations(next)) {
116+
String qName = a.getQualifiedName();
117+
if (!seen.contains(qName)) {
118+
seen.add(qName);
119+
queue.add(a);
120+
}
121+
}
122+
return next;
123+
}
124+
125+
};
126+
}
84127

85128
public static Collection<ITypeBinding> getMetaAnnotations(ITypeBinding actualAnnotation, Predicate<String> isKeyAnnotationName) {
86129
synchronized(lock) {

headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchiesTests.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public boolean visit(MarkerAnnotation node) {
111111

112112
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "test.CustomComponent2")).isTrue();
113113
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.context.annotation.Configuration")).isFalse();
114-
assertThat(AnnotationHierarchies.isMetaAnnotation(binding, "test.CustomComponent2"::equals)).isTrue();
114+
assertThat(AnnotationHierarchies.getMetaAnnotations(binding, qn -> true).stream().toList().size()).isEqualTo(3);
115115
assertThat(AnnotationHierarchies.isMetaAnnotation(binding, "org.springframework.context.annotation.Configuration"::equals)).isFalse();
116116
assertThat(AnnotationHierarchies.getDirectSuperAnnotations(binding).stream().toList().size()).isEqualTo(2);
117117

@@ -129,4 +129,56 @@ public boolean visit(MarkerAnnotation node) {
129129
}, null);
130130

131131
}
132+
133+
@Test
134+
void simpleHierarchy() throws Exception {
135+
String projectName = "test-spring-validations";
136+
IJavaProject project = ProjectsHarness.INSTANCE.mavenProject(projectName);
137+
Path file = createFile(projectName, "test", "MyComponent.java", """
138+
package test;
139+
140+
import org.springframework.boot.autoconfigure.SpringBootApplication
141+
142+
@SpringBootApplication
143+
public class MyComponent {
144+
145+
}
146+
""");
147+
148+
SpringIndexerJava.createParser(project, true).createASTs(new String[] { file.toFile().toString() }, null, new String[0], new FileASTRequestor() {
149+
@Override
150+
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
151+
cu.accept(new ASTVisitor() {
152+
153+
@Override
154+
public boolean visit(MarkerAnnotation node) {
155+
ITypeBinding binding = node.resolveTypeBinding();
156+
assertThat(binding).isNotNull();
157+
assertThat(binding.getQualifiedName()).isEqualTo("org.springframework.boot.autoconfigure.SpringBootApplication");
158+
159+
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "test.CustomComponent2")).isFalse();
160+
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.context.annotation.Configuration")).isTrue();
161+
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.boot.autoconfigure.SpringBootApplication")).isTrue();
162+
assertThat(AnnotationHierarchies.hasTransitiveSuperAnnotationType(binding, "org.springframework.stereotype.Component")).isTrue();
163+
List<ITypeBinding> metaAnnotations = AnnotationHierarchies.getMetaAnnotations(binding, qn -> true).stream().toList();
164+
assertThat(metaAnnotations.size()).isEqualTo(8);
165+
assertThat(AnnotationHierarchies.isMetaAnnotation(binding, "org.springframework.context.annotation.Configuration"::equals)).isTrue();
166+
assertThat(AnnotationHierarchies.getDirectSuperAnnotations(binding).stream().toList().size()).isEqualTo(3);
167+
168+
IAnnotationBinding annotationBinding = node.resolveAnnotationBinding();
169+
assertThat(annotationBinding).isNotNull();
170+
assertThat(AnnotationHierarchies.getDirectSuperAnnotationBindings(annotationBinding).stream().toList().size()).isEqualTo(3);
171+
assertThat(AnnotationHierarchies.isSubtypeOf(node, "test.CustomComponent2")).isFalse();
172+
assertThat(AnnotationHierarchies.isSubtypeOf(node, "org.springframework.context.annotation.Configuration")).isTrue();
173+
assertThat(AnnotationHierarchies.isSubtypeOf(node, "org.springframework.boot.autoconfigure.SpringBootApplication")).isTrue();
174+
assertThat(AnnotationHierarchies.isSubtypeOf(node, "org.springframework.stereotype.Component")).isTrue();
175+
return super.visit(node);
176+
}
177+
178+
});
179+
}
180+
}, null);
181+
182+
}
183+
132184
}

0 commit comments

Comments
 (0)