Skip to content

Commit

Permalink
GH-1462: add event listener index elements for interface implementati…
Browse files Browse the repository at this point in the history
…ons without component annotation
  • Loading branch information
martinlippert committed Feb 5, 2025
1 parent 168637d commit 12c50b5
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ public void removeElements(String projectName, String docURI) {
project.removeDocument(docURI);
}
}

public DocumentElement getDocument(String docURI) {
ArrayDeque<SpringIndexElement> elementsToVisit = new ArrayDeque<>();
elementsToVisit.addAll(this.projectRootElements.values());

while (!elementsToVisit.isEmpty()) {
SpringIndexElement element = elementsToVisit.pop();

if (element instanceof DocumentElement doc && doc.getDocURI().equals(docURI)) {
return doc;
}

elementsToVisit.addAll(element.getChildren());
}

return null;
}

public Bean[] getBeans() {
List<Bean> result = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.events.EventListenerIndexElement;
import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
Expand Down Expand Up @@ -131,7 +132,7 @@ protected void createSymbol(Annotation node, ITypeBinding annotationType, Collec
Collection<Annotation> annotationsOnHandleEventMethod = ASTUtils.getAnnotations(handleEventMethod);
AnnotationMetadata[] handleEventMethodAnnotations = ASTUtils.getAnnotationsMetadata(annotationsOnHandleEventMethod, doc);

EventListenerIndexElement eventElement = new EventListenerIndexElement(eventTypeFq, handleMethodLocation, handleEventMethodAnnotations);
EventListenerIndexElement eventElement = new EventListenerIndexElement(eventTypeFq, handleMethodLocation, beanType.getQualifiedName(), handleEventMethodAnnotations);
beanDefinition.addChild(eventElement);
}
}
Expand Down Expand Up @@ -198,6 +199,40 @@ public boolean visit(MethodInvocation methodInvocation) {
}
});
}

@Override
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
// event listener - create child element, if necessary
try {
ITypeBinding typeBinding = typeDeclaration.resolveBinding();
if (typeBinding == null) return;

ITypeBinding inTypeHierarchy = ASTUtils.findInTypeHierarchy(typeDeclaration, doc, typeBinding, Set.of(Annotations.APPLICATION_LISTENER));
if (inTypeHierarchy == null) return;

MethodDeclaration handleEventMethod = findHandleEventMethod(typeDeclaration);
if (handleEventMethod == null) return;

IMethodBinding methodBinding = handleEventMethod.resolveBinding();
ITypeBinding[] parameterTypes = methodBinding.getParameterTypes();
if (parameterTypes != null && parameterTypes.length == 1) {

ITypeBinding eventType = parameterTypes[0];
String eventTypeFq = eventType.getQualifiedName();

DocumentRegion nodeRegion = ASTUtils.nodeRegion(doc, handleEventMethod.getName());
Location handleMethodLocation = new Location(doc.getUri(), nodeRegion.asRange());

Collection<Annotation> annotationsOnHandleEventMethod = ASTUtils.getAnnotations(handleEventMethod);
AnnotationMetadata[] handleEventMethodAnnotations = ASTUtils.getAnnotationsMetadata(annotationsOnHandleEventMethod, doc);

EventListenerIndexElement eventElement = new EventListenerIndexElement(eventTypeFq, handleMethodLocation, typeBinding.getQualifiedName(), handleEventMethodAnnotations);
context.getBeans().add(new CachedBean(doc.getUri(), eventElement));
}
} catch (BadLocationException e) {
log.error("", e);
}
}

private MethodDeclaration findHandleEventMethod(TypeDeclaration type) {
MethodDeclaration[] methods = type.getMethods();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ public class EventListenerIndexElement extends AbstractSpringIndexElement {

private final String eventType;
private final Location location;
private final String containerBeanType;
private final AnnotationMetadata[] annotations;

public EventListenerIndexElement(String eventType, Location location, AnnotationMetadata[] annotations) {
public EventListenerIndexElement(String eventType, Location location, String containerBeanType, AnnotationMetadata[] annotations) {
this.eventType = eventType;
this.location = location;
this.containerBeanType = containerBeanType;
this.annotations = annotations;
}

Expand All @@ -41,4 +43,8 @@ public Location getLocation() {
return location;
}

public String getContainerBeanType() {
return containerBeanType;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
Expand Down Expand Up @@ -75,15 +76,29 @@ protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding, Collec
ITypeBinding eventType = getEventType(node, method);
DocumentRegion nodeRegion = ASTUtils.nodeRegion(doc, method.getName());
Location location = new Location(doc.getUri(), nodeRegion.asRange());

String containerBeanType = null;
TypeDeclaration type = ASTUtils.findDeclaringType(method);
if (type != null) {
ITypeBinding binding = type.resolveBinding();
if (binding != null) {
containerBeanType = binding.getQualifiedName();
}
}

cachedBean.getBean().addChild(new EventListenerIndexElement(eventType != null ? eventType.getQualifiedName() : "", location, annotations));
cachedBean.getBean().addChild(new EventListenerIndexElement(eventType != null ? eventType.getQualifiedName() : "", location, containerBeanType, annotations));
}
}
} catch (BadLocationException e) {
log.error("", e);
}
}

@Override
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
super.addSymbolsPass1(typeDeclaration, context, doc);
}

private String createEventListenerSymbolLabel(Annotation node, MethodDeclaration method) {
// event listener annotation type
String annotationTypeName = getAnnotationTypeName(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.DocumentElement;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
Expand Down Expand Up @@ -117,6 +118,7 @@ void testAnnotationBasedEventListenerIndexElements() throws Exception {

EventListenerIndexElement listenerElement = (EventListenerIndexElement) children.get(0);
assertEquals("org.springframework.context.ApplicationEvent", listenerElement.getEventType());
assertEquals("com.example.events.demo.EventListenerPerAnnotation", listenerElement.getContainerBeanType());

Location location = listenerElement.getLocation();
assertNotNull(location);
Expand All @@ -140,13 +142,34 @@ void testEventListenerIndexElementForEventListenerInterfaceImplementation() thro

EventListenerIndexElement listenerElement = (EventListenerIndexElement) children.get(0);
assertEquals("org.springframework.context.ApplicationEvent", listenerElement.getEventType());
assertEquals("com.example.events.demo.EventListenerPerInterface", listenerElement.getContainerBeanType());

Location location = listenerElement.getLocation();
assertNotNull(location);
assertEquals(docUri, location.getUri());
assertEquals(new Range(new Position(10, 13), new Position(10, 31)), location.getRange());
}

@Test
void testEventListenerIndexElementForListenerInterfaceImplementationWithoutComponentAnnotation() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/com/example/events/demo/EventListenerPerInterfaceAndBeanMethod.java").toUri().toString();

Bean[] beans = springIndex.getBeansOfDocument(docUri);
assertEquals(0, beans.length);

DocumentElement document = springIndex.getDocument(docUri);
List<SpringIndexElement> children = document.getChildren();

EventListenerIndexElement listenerElement = children.stream().filter(element -> element instanceof EventListenerIndexElement).map(element -> (EventListenerIndexElement) element).findFirst().get();
assertEquals("org.springframework.context.ApplicationEvent", listenerElement.getEventType());
assertEquals("com.example.events.demo.EventListenerPerInterfaceAndBeanMethod", listenerElement.getContainerBeanType());

Location location = listenerElement.getLocation();
assertNotNull(location);
assertEquals(docUri, location.getUri());
assertEquals(new Range(new Position(8, 13), new Position(8, 31)), location.getRange());
}

@Test
void testEventPublisherIndexElements() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/com/example/events/demo/CustomEventPublisher.java").toUri().toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

<build>
Expand Down

0 comments on commit 12c50b5

Please sign in to comment.