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 4a326c608..5806152d5 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 @@ -34,7 +34,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; 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; @@ -112,7 +111,7 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider { Bean beanDefinition = new Bean(beanName, beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, isConfiguration); - // event listener - create child element, if necessary + // type implements event listener - move those already created event index elements under the bean node List alreadyCreatedEventListenerChilds = context.getBeans().stream() .filter(cachedBean -> cachedBean.getDocURI().equals(doc.getUri())) .filter(cachedBean -> cachedBean.getBean() instanceof EventListenerIndexElement) @@ -123,31 +122,6 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider { beanDefinition.addChild(eventListener.getBean()); } -// ITypeBinding inTypeHierarchy = ASTUtils.findInTypeHierarchy(type, doc, beanType, Set.of(Annotations.APPLICATION_LISTENER)); -// if (inTypeHierarchy != null) { -// -// MethodDeclaration handleEventMethod = findHandleEventMethod(type); -// if (handleEventMethod != null) { -// -// 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 annotationsOnHandleEventMethod = ASTUtils.getAnnotations(handleEventMethod); -// AnnotationMetadata[] handleEventMethodAnnotations = ASTUtils.getAnnotationsMetadata(annotationsOnHandleEventMethod, doc); -// -// EventListenerIndexElement eventElement = new EventListenerIndexElement(eventTypeFq, handleMethodLocation, beanType.getQualifiedName(), handleEventMethodAnnotations); -// beanDefinition.addChild(eventElement); -// } -// } -// } - // event publisher checks for (InjectionPoint injectionPoint : injectionPoints) { if (Annotations.EVENT_PUBLISHER.equals(injectionPoint.getType())) { @@ -212,7 +186,6 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider { @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; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/events/EventReferenceProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/events/EventReferenceProvider.java index 45d077164..8cbe36f80 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/events/EventReferenceProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/events/EventReferenceProvider.java @@ -10,9 +10,12 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.events; +import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; @@ -25,6 +28,8 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue; +import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -55,19 +60,26 @@ public class EventReferenceProvider implements ReferenceProvider { List listeners = index.getNodesOfType(EventListenerIndexElement.class); List publishers = index.getNodesOfType(EventPublisherIndexElement.class); - // when offset is inside an event listener, find the respective event type - Optional listenerEventType = listeners.stream() + // when offset is inside an event listener, look for references from publishers + Optional listenerElement = listeners.stream() .filter(listener -> listener.getLocation().getUri().equals(doc.getUri())) .filter(eventListener -> isPositionInside(position, eventListener.getLocation())) - .map(eventListener -> eventListener.getEventType()) .findAny(); - if (listenerEventType.isPresent()) { - // use the listener event type to look for publishers for that type - String eventType = listenerEventType.get(); + if (listenerElement.isPresent()) { + Set eventTypes = getListenerEventTypes(listenerElement.get()); List foundLocations = publishers.stream() - .filter(publisher -> publisher.getEventType().equals(eventType) || publisher.getEventTypesFromHierarchy().contains(eventType)) + .filter(publisher -> { + if (eventTypes.contains(publisher.getEventType())) return true; + + for (String listenerEventType : eventTypes) { + if (publisher.getEventTypesFromHierarchy().contains(listenerEventType)) { + return true; + } + } + return false; + }) .map(publisher -> publisher.getLocation()) .toList(); @@ -76,7 +88,7 @@ public class EventReferenceProvider implements ReferenceProvider { } } - // when offset is inside an event publisher, find the respective event type + // when offset is inside an event publisher, look for references from listeners else { Optional publisherElement = publishers.stream() .filter(publisher -> publisher.getLocation().getUri().equals(doc.getUri())) @@ -84,12 +96,22 @@ public class EventReferenceProvider implements ReferenceProvider { .findAny(); if (publisherElement.isPresent()) { - // use the publisher event type to look for listeners for that type String eventType = publisherElement.get().getEventType(); Set eventTypesFromHierarchy = publisherElement.get().getEventTypesFromHierarchy(); List foundLocations = listeners.stream() - .filter(listener -> listener.getEventType().equals(eventType) || eventTypesFromHierarchy.contains(listener.getEventType())) + .filter(listener -> { + Set listenerEventTypes = getListenerEventTypes(listener); + for (String listenerEventType : listenerEventTypes) { + if (listenerEventType.equals(eventType)) { + return true; + } + if (eventTypesFromHierarchy.contains(listenerEventType)) { + return true; + } + } + return false; + }) .map(listener -> listener.getLocation()) .toList(); @@ -106,6 +128,29 @@ public class EventReferenceProvider implements ReferenceProvider { return null; } + private Set getListenerEventTypes(EventListenerIndexElement eventListenerIndexElement) { + AnnotationMetadata[] annotations = eventListenerIndexElement.getAnnotations(); + if (annotations != null && annotations.length > 0) { + for (AnnotationMetadata annotationMetadata : annotations) { + Map attributes = annotationMetadata.getAttributes(); + if (attributes.containsKey("classes")) { + AnnotationAttributeValue[] annotationAttributeValues = attributes.get("classes"); + return Arrays.stream(annotationAttributeValues) + .map(attributeValue -> attributeValue.getName()) + .collect(Collectors.toSet()); + } + else if (attributes.containsKey("value")) { + AnnotationAttributeValue[] annotationAttributeValues = attributes.get("value"); + return Arrays.stream(annotationAttributeValues) + .map(attributeValue -> attributeValue.getName()) + .collect(Collectors.toSet()); + } + } + } + + return Set.of(eventListenerIndexElement.getEventType()); + } + private boolean isPositionInside(Position position, Location location) { boolean afterStart = position.getLine() > location.getRange().getStart().getLine() || (position.getLine() == location.getRange().getStart().getLine() && position.getCharacter() >= location.getRange().getStart().getCharacter()); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/events/test/EventsReferencesProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/events/test/EventsReferencesProviderTest.java index ee1e22ea7..3d4db8872 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/events/test/EventsReferencesProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/events/test/EventsReferencesProviderTest.java @@ -169,11 +169,9 @@ public class EventsReferencesProviderTest { assertTrue(references.contains(expectedLocation2)); String expectedDefinitionUri3 = directory.toPath().resolve("src/main/java/com/example/events/demo/EventListenerPerInterfaceAndBeanMethod.java").toUri().toString(); - Location expectedLocation3 = new Location(expectedDefinitionUri3, new Range(new Position(9, 13), new Position(9, 24))); + Location expectedLocation3 = new Location(expectedDefinitionUri3, new Range(new Position(7, 13), new Position(7, 31))); assertTrue(references.contains(expectedLocation3)); - - } }