Skip to content

Commit f8aabc2

Browse files
committed
GH-1465: take event listener types defined via annotation attribute into account when looking for references
Fixes GH-1465
1 parent 24c5d4e commit f8aabc2

File tree

3 files changed

+57
-41
lines changed

3 files changed

+57
-41
lines changed

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

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.slf4j.Logger;
3535
import org.slf4j.LoggerFactory;
3636
import org.springframework.ide.vscode.boot.java.Annotations;
37-
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
3837
import org.springframework.ide.vscode.boot.java.events.EventListenerIndexElement;
3938
import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement;
4039
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
@@ -112,7 +111,7 @@ protected void createSymbol(Annotation node, ITypeBinding annotationType, Collec
112111

113112
Bean beanDefinition = new Bean(beanName, beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, isConfiguration);
114113

115-
// event listener - create child element, if necessary
114+
// type implements event listener - move those already created event index elements under the bean node
116115
List<CachedBean> alreadyCreatedEventListenerChilds = context.getBeans().stream()
117116
.filter(cachedBean -> cachedBean.getDocURI().equals(doc.getUri()))
118117
.filter(cachedBean -> cachedBean.getBean() instanceof EventListenerIndexElement)
@@ -123,31 +122,6 @@ protected void createSymbol(Annotation node, ITypeBinding annotationType, Collec
123122
beanDefinition.addChild(eventListener.getBean());
124123
}
125124

126-
// ITypeBinding inTypeHierarchy = ASTUtils.findInTypeHierarchy(type, doc, beanType, Set.of(Annotations.APPLICATION_LISTENER));
127-
// if (inTypeHierarchy != null) {
128-
//
129-
// MethodDeclaration handleEventMethod = findHandleEventMethod(type);
130-
// if (handleEventMethod != null) {
131-
//
132-
// IMethodBinding methodBinding = handleEventMethod.resolveBinding();
133-
// ITypeBinding[] parameterTypes = methodBinding.getParameterTypes();
134-
// if (parameterTypes != null && parameterTypes.length == 1) {
135-
//
136-
// ITypeBinding eventType = parameterTypes[0];
137-
// String eventTypeFq = eventType.getQualifiedName();
138-
//
139-
// DocumentRegion nodeRegion = ASTUtils.nodeRegion(doc, handleEventMethod.getName());
140-
// Location handleMethodLocation = new Location(doc.getUri(), nodeRegion.asRange());
141-
//
142-
// Collection<Annotation> annotationsOnHandleEventMethod = ASTUtils.getAnnotations(handleEventMethod);
143-
// AnnotationMetadata[] handleEventMethodAnnotations = ASTUtils.getAnnotationsMetadata(annotationsOnHandleEventMethod, doc);
144-
//
145-
// EventListenerIndexElement eventElement = new EventListenerIndexElement(eventTypeFq, handleMethodLocation, beanType.getQualifiedName(), handleEventMethodAnnotations);
146-
// beanDefinition.addChild(eventElement);
147-
// }
148-
// }
149-
// }
150-
151125
// event publisher checks
152126
for (InjectionPoint injectionPoint : injectionPoints) {
153127
if (Annotations.EVENT_PUBLISHER.equals(injectionPoint.getType())) {
@@ -212,7 +186,6 @@ public boolean visit(MethodInvocation methodInvocation) {
212186

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

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

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.events;
1212

13+
import java.util.Arrays;
1314
import java.util.List;
15+
import java.util.Map;
1416
import java.util.Optional;
1517
import java.util.Set;
18+
import java.util.stream.Collectors;
1619

1720
import org.eclipse.jdt.core.dom.ASTNode;
1821
import org.eclipse.jdt.core.dom.Annotation;
@@ -25,6 +28,8 @@
2528
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
2629
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
2730
import org.springframework.ide.vscode.commons.java.IJavaProject;
31+
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue;
32+
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
2833
import org.springframework.ide.vscode.commons.util.BadLocationException;
2934
import org.springframework.ide.vscode.commons.util.text.TextDocument;
3035

@@ -55,19 +60,26 @@ public List<? extends Location> provideReferences(CancelChecker cancelToken, IJa
5560
List<EventListenerIndexElement> listeners = index.getNodesOfType(EventListenerIndexElement.class);
5661
List<EventPublisherIndexElement> publishers = index.getNodesOfType(EventPublisherIndexElement.class);
5762

58-
// when offset is inside an event listener, find the respective event type
59-
Optional<String> listenerEventType = listeners.stream()
63+
// when offset is inside an event listener, look for references from publishers
64+
Optional<EventListenerIndexElement> listenerElement = listeners.stream()
6065
.filter(listener -> listener.getLocation().getUri().equals(doc.getUri()))
6166
.filter(eventListener -> isPositionInside(position, eventListener.getLocation()))
62-
.map(eventListener -> eventListener.getEventType())
6367
.findAny();
6468

65-
if (listenerEventType.isPresent()) {
66-
// use the listener event type to look for publishers for that type
67-
String eventType = listenerEventType.get();
69+
if (listenerElement.isPresent()) {
70+
Set<String> eventTypes = getListenerEventTypes(listenerElement.get());
6871

6972
List<Location> foundLocations = publishers.stream()
70-
.filter(publisher -> publisher.getEventType().equals(eventType) || publisher.getEventTypesFromHierarchy().contains(eventType))
73+
.filter(publisher -> {
74+
if (eventTypes.contains(publisher.getEventType())) return true;
75+
76+
for (String listenerEventType : eventTypes) {
77+
if (publisher.getEventTypesFromHierarchy().contains(listenerEventType)) {
78+
return true;
79+
}
80+
}
81+
return false;
82+
})
7183
.map(publisher -> publisher.getLocation())
7284
.toList();
7385

@@ -76,20 +88,30 @@ public List<? extends Location> provideReferences(CancelChecker cancelToken, IJa
7688
}
7789
}
7890

79-
// when offset is inside an event publisher, find the respective event type
91+
// when offset is inside an event publisher, look for references from listeners
8092
else {
8193
Optional<EventPublisherIndexElement> publisherElement = publishers.stream()
8294
.filter(publisher -> publisher.getLocation().getUri().equals(doc.getUri()))
8395
.filter(eventPublisher -> isPositionInside(position, eventPublisher.getLocation()))
8496
.findAny();
8597

8698
if (publisherElement.isPresent()) {
87-
// use the publisher event type to look for listeners for that type
8899
String eventType = publisherElement.get().getEventType();
89100
Set<String> eventTypesFromHierarchy = publisherElement.get().getEventTypesFromHierarchy();
90101

91102
List<Location> foundLocations = listeners.stream()
92-
.filter(listener -> listener.getEventType().equals(eventType) || eventTypesFromHierarchy.contains(listener.getEventType()))
103+
.filter(listener -> {
104+
Set<String> listenerEventTypes = getListenerEventTypes(listener);
105+
for (String listenerEventType : listenerEventTypes) {
106+
if (listenerEventType.equals(eventType)) {
107+
return true;
108+
}
109+
if (eventTypesFromHierarchy.contains(listenerEventType)) {
110+
return true;
111+
}
112+
}
113+
return false;
114+
})
93115
.map(listener -> listener.getLocation())
94116
.toList();
95117

@@ -106,6 +128,29 @@ public List<? extends Location> provideReferences(CancelChecker cancelToken, IJa
106128
return null;
107129
}
108130

131+
private Set<String> getListenerEventTypes(EventListenerIndexElement eventListenerIndexElement) {
132+
AnnotationMetadata[] annotations = eventListenerIndexElement.getAnnotations();
133+
if (annotations != null && annotations.length > 0) {
134+
for (AnnotationMetadata annotationMetadata : annotations) {
135+
Map<String, AnnotationAttributeValue[]> attributes = annotationMetadata.getAttributes();
136+
if (attributes.containsKey("classes")) {
137+
AnnotationAttributeValue[] annotationAttributeValues = attributes.get("classes");
138+
return Arrays.stream(annotationAttributeValues)
139+
.map(attributeValue -> attributeValue.getName())
140+
.collect(Collectors.toSet());
141+
}
142+
else if (attributes.containsKey("value")) {
143+
AnnotationAttributeValue[] annotationAttributeValues = attributes.get("value");
144+
return Arrays.stream(annotationAttributeValues)
145+
.map(attributeValue -> attributeValue.getName())
146+
.collect(Collectors.toSet());
147+
}
148+
}
149+
}
150+
151+
return Set.of(eventListenerIndexElement.getEventType());
152+
}
153+
109154
private boolean isPositionInside(Position position, Location location) {
110155
boolean afterStart = position.getLine() > location.getRange().getStart().getLine()
111156
|| (position.getLine() == location.getRange().getStart().getLine() && position.getCharacter() >= location.getRange().getStart().getCharacter());

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,9 @@ public void foo() {
169169
assertTrue(references.contains(expectedLocation2));
170170

171171
String expectedDefinitionUri3 = directory.toPath().resolve("src/main/java/com/example/events/demo/EventListenerPerInterfaceAndBeanMethod.java").toUri().toString();
172-
Location expectedLocation3 = new Location(expectedDefinitionUri3, new Range(new Position(9, 13), new Position(9, 24)));
172+
Location expectedLocation3 = new Location(expectedDefinitionUri3, new Range(new Position(7, 13), new Position(7, 31)));
173173

174174
assertTrue(references.contains(expectedLocation3));
175-
176-
177175
}
178176

179177
}

0 commit comments

Comments
 (0)