Skip to content

Commit 21accae

Browse files
committed
GH-1041: replaced webflux symbol addon information objects with new index child nodes
some additional groundwork for nested structure inside of spring index, in support of GH-1425, GH-1431, GH-1424
1 parent c1b1a06 commit 21accae

File tree

17 files changed

+481
-349
lines changed

17 files changed

+481
-349
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Broadcom
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.commons.protocol.spring;
12+
13+
public abstract class AbstractSpringIndexElement implements SpringIndexElement {
14+
15+
public static final SpringIndexElement[] NO_CHILDREN = new SpringIndexElement[0];
16+
17+
private final SpringIndexElement[] children;
18+
19+
public AbstractSpringIndexElement(SpringIndexElement[] children) {
20+
this.children = children != null ? children : NO_CHILDREN;
21+
}
22+
23+
@Override
24+
public SpringIndexElement[] getChildren() {
25+
return children;
26+
}
27+
28+
}

headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/Bean.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import com.google.gson.Gson;
1818

19-
public class Bean {
19+
public class Bean extends AbstractSpringIndexElement {
2020

2121
private final String name;
2222
private final String type;
@@ -32,7 +32,11 @@ public Bean(
3232
Location location,
3333
InjectionPoint[] injectionPoints,
3434
Set<String> supertypes,
35-
AnnotationMetadata[] annotations, boolean isConfiguration) {
35+
AnnotationMetadata[] annotations,
36+
boolean isConfiguration,
37+
SpringIndexElement[] children) {
38+
39+
super(children);
3640

3741
this.name = name;
3842
this.type = type;
@@ -64,6 +68,18 @@ else if (supertypes != null && supertypes.size() == 1 && supertypes.contains("ja
6468
}
6569
}
6670

71+
public Bean(
72+
String name,
73+
String type,
74+
Location location,
75+
InjectionPoint[] injectionPoints,
76+
Set<String> supertypes,
77+
AnnotationMetadata[] annotations,
78+
boolean isConfiguration) {
79+
this(name, type, location, injectionPoints, supertypes, annotations, isConfiguration, AbstractSpringIndexElement.NO_CHILDREN);
80+
}
81+
82+
6783
public String getName() {
6884
return name;
6985
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Broadcom
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.commons.protocol.spring;
12+
13+
public interface SpringIndexElement {
14+
15+
SpringIndexElement[] getChildren();
16+
17+
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.springframework.ide.vscode.boot.java.data.DataRepositorySymbolProvider;
2222
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
2323
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider;
24-
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
2524
import org.springframework.ide.vscode.boot.java.utils.RestrictedDefaultSymbolProvider;
2625

2726
@Configuration(proxyBeanMethods = false)
@@ -36,7 +35,6 @@ AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache)
3635
ComponentSymbolProvider componentSymbolProvider = new ComponentSymbolProvider();
3736
RestrictedDefaultSymbolProvider restrictedDefaultSymbolProvider = new RestrictedDefaultSymbolProvider();
3837
DataRepositorySymbolProvider dataRepositorySymbolProvider = new DataRepositorySymbolProvider();
39-
WebfluxRouterSymbolProvider webfluxRouterSymbolProvider = new WebfluxRouterSymbolProvider();
4038

4139
providers.put(Annotations.SPRING_REQUEST_MAPPING, requestMappingSymbolProvider);
4240
providers.put(Annotations.SPRING_GET_MAPPING, requestMappingSymbolProvider);
@@ -72,7 +70,6 @@ AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache)
7270
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, restrictedDefaultSymbolProvider);
7371

7472
providers.put(Annotations.REPOSITORY, dataRepositorySymbolProvider);
75-
providers.put("", webfluxRouterSymbolProvider);
7673

7774
providers.put(Annotations.FEIGN_CLIENT, new FeignClientSymbolProvider());
7875

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/index/cache/IndexCacheOnDiscDeltaBased.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
4747
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
4848
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
49+
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
4950
import org.springframework.ide.vscode.commons.util.UriUtil;
5051

5152
import com.google.common.collect.ImmutableMultimap;
@@ -374,6 +375,7 @@ public static Gson createGson() {
374375
.registerTypeAdapter(Bean.class, new BeanJsonAdapter())
375376
.registerTypeAdapter(InjectionPoint.class, new InjectionPointJsonAdapter())
376377
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
378+
.registerTypeAdapter(SpringIndexElement.class, new SpringIndexElementAdapter())
377379
.create();
378380
}
379381

@@ -575,6 +577,7 @@ public IndexCacheStore<T> apply(IndexCacheStore<T> store) {
575577
// GSON serialize / deserialize adapters for the various types involved here that have special needs around JSON
576578
//
577579
//
580+
578581

579582
private static class IndexCacheStoreAdapter implements JsonDeserializer<IndexCacheStore<?>> {
580583

@@ -610,9 +613,6 @@ public IndexCacheStore<?> deserialize(JsonElement json, Type typeOfT, JsonDeseri
610613

611614
}
612615

613-
/**
614-
* gson adapter to store subtype information for symbol addon informations
615-
*/
616616
private static class DeltaStorageAdapter implements JsonSerializer<DeltaStorage<?>>, JsonDeserializer<DeltaStorage<?>> {
617617

618618
@Override
@@ -637,9 +637,6 @@ public DeltaStorage<?> deserialize(JsonElement json, Type type, JsonDeserializat
637637
}
638638
}
639639

640-
/**
641-
* gson adapter to store subtype information for symbol addon informations
642-
*/
643640
private static class SymbolAddOnInformationAdapter implements JsonSerializer<SymbolAddOnInformation>, JsonDeserializer<SymbolAddOnInformation> {
644641

645642
@Override
@@ -664,9 +661,6 @@ public SymbolAddOnInformation deserialize(JsonElement json, Type type, JsonDeser
664661
}
665662
}
666663

667-
/**
668-
* gson adapter to store subtype information for beans
669-
*/
670664
private static class BeanJsonAdapter implements JsonDeserializer<Bean> {
671665

672666
@Override
@@ -690,8 +684,11 @@ public Bean deserialize(JsonElement json, Type type, JsonDeserializationContext
690684

691685
JsonElement isConfigurationObject = parsedObject.get("isConfiguration");
692686
boolean isConfiguration = context.deserialize(isConfigurationObject, boolean.class);
687+
688+
JsonElement childrenObject = parsedObject.get("children");
689+
SpringIndexElement[] children = context.deserialize(childrenObject, SpringIndexElement[].class);
693690

694-
return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations, isConfiguration);
691+
return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations, isConfiguration, children);
695692
}
696693
}
697694

@@ -714,4 +711,27 @@ public InjectionPoint deserialize(JsonElement json, Type type, JsonDeserializati
714711
}
715712
}
716713

714+
private static class SpringIndexElementAdapter implements JsonSerializer<SpringIndexElement>, JsonDeserializer<SpringIndexElement> {
715+
716+
@Override
717+
public JsonElement serialize(SpringIndexElement element, Type typeOfSrc, JsonSerializationContext context) {
718+
JsonElement elem = context.serialize(element);
719+
elem.getAsJsonObject().addProperty("type", element.getClass().getName());
720+
return elem;
721+
}
722+
723+
@Override
724+
public SpringIndexElement deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
725+
JsonObject jsonObject = json.getAsJsonObject();
726+
String typeName = jsonObject.get("type").getAsString();
727+
728+
try {
729+
return context.deserialize(jsonObject, (Class<?>) Class.forName(typeName));
730+
} catch (ClassNotFoundException e) {
731+
throw new JsonParseException(e);
732+
}
733+
}
734+
}
735+
736+
717737
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public BootJavaLanguageServerComponents(ApplicationContext appContext) {
187187

188188
spelSemanticTokens = appContext.getBean(SpelSemanticTokens.class);
189189

190-
codeLensHandler = createCodeLensEngine(springSymbolIndex, projectFinder, server, spelSemanticTokens);
190+
codeLensHandler = createCodeLensEngine(springIndex, projectFinder, server, spelSemanticTokens);
191191

192192
highlightsEngine = createDocumentHighlightEngine(appContext);
193193
documents.onDocumentHighlight(highlightsEngine);
@@ -328,17 +328,17 @@ protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server,
328328
return new BootJavaReferencesHandler(this, cuCache, projectFinder, providers);
329329
}
330330

331-
protected BootJavaCodeLensEngine createCodeLensEngine(SpringSymbolIndex index, JavaProjectFinder projectFinder, SimpleLanguageServer server, SpelSemanticTokens spelSemanticTokens) {
331+
protected BootJavaCodeLensEngine createCodeLensEngine(SpringMetamodelIndex springIndex, JavaProjectFinder projectFinder, SimpleLanguageServer server, SpelSemanticTokens spelSemanticTokens) {
332332
Collection<CodeLensProvider> codeLensProvider = new ArrayList<>();
333-
codeLensProvider.add(new WebfluxHandlerCodeLensProvider(index));
333+
codeLensProvider.add(new WebfluxHandlerCodeLensProvider(springIndex));
334334
codeLensProvider.add(new CopilotCodeLensProvider(projectFinder, server, spelSemanticTokens));
335335

336336
return new BootJavaCodeLensEngine(this, codeLensProvider);
337337
}
338338

339339
protected BootJavaDocumentHighlightEngine createDocumentHighlightEngine(ApplicationContext appContext) {
340340
Collection<HighlightProvider> highlightProvider = new ArrayList<>();
341-
highlightProvider.add(new WebfluxRouteHighlightProdivder(appContext.getBean(SpringSymbolIndex.class)));
341+
highlightProvider.add(new WebfluxRouteHighlightProdivder(appContext.getBean(SpringMetamodelIndex.class)));
342342

343343
Map<String, JdtAstDocHighlightsProvider> astHighlightProviders = appContext.getBeansOfType(JdtAstDocHighlightsProvider.class);
344344
if (!astHighlightProviders.isEmpty()) {

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.beans;
1212

13+
import java.util.ArrayList;
1314
import java.util.Collection;
1415
import java.util.HashSet;
1516
import java.util.List;
@@ -34,13 +35,16 @@
3435
import org.springframework.ide.vscode.boot.java.Annotations;
3536
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
3637
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
38+
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
3739
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
3840
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
3941
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
4042
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
43+
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
4144
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
4245
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
4346
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
47+
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
4448
import org.springframework.ide.vscode.commons.util.BadLocationException;
4549
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
4650
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -57,13 +61,12 @@
5761
*/
5862
public class BeansSymbolProvider extends AbstractSymbolProvider {
5963

60-
6164
private static final Logger log = LoggerFactory.getLogger(BeansSymbolProvider.class);
6265

6366
private static final String[] NAME_ATTRIBUTES = {"value", "name"};
6467

6568
@Override
66-
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
69+
public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
6770
if (node == null) return;
6871

6972
ASTNode parent = node.getParent();
@@ -73,7 +76,22 @@ protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Col
7376

7477
if (isMethodAbstract(method)) return;
7578

79+
boolean isWebfluxRouter = WebfluxRouterSymbolProvider.isWebfluxRouterBean(method);
80+
81+
// for webflux details, we need full method body ASTs
82+
if (isWebfluxRouter && SCAN_PASS.ONE.equals(context.getPass())) {
83+
context.getNextPassFiles().add(context.getFile());
84+
return;
85+
}
86+
87+
List<SpringIndexElement> childElements = new ArrayList<>();
88+
89+
if (isWebfluxRouter) {
90+
WebfluxRouterSymbolProvider.createWebfluxElements(method, context, doc, childElements);
91+
}
92+
7693
boolean isFunction = isFunctionBean(method);
94+
7795
ITypeBinding beanType = getBeanType(method);
7896
String markerString = getAnnotations(method);
7997

@@ -97,7 +115,7 @@ protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Col
97115
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(method);
98116
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnMethod, doc);
99117

100-
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, false);
118+
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, false, childElements.toArray(SpringIndexElement[]::new));
101119

102120
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
103121
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));

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

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2018, 2021 Pivotal, Inc.
2+
* Copyright (c) 2018, 2024 Pivotal, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -9,7 +9,7 @@
99
* Pivotal, Inc. - initial API and implementation
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.requestmapping;
12-
12+
import java.util.Arrays;
1313
import java.util.List;
1414

1515
import org.eclipse.jdt.core.dom.ASTVisitor;
@@ -19,9 +19,9 @@
1919
import org.eclipse.lsp4j.CodeLens;
2020
import org.eclipse.lsp4j.Command;
2121
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
22-
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
22+
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
2323
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
24-
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
24+
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
2525
import org.springframework.ide.vscode.commons.util.BadLocationException;
2626
import org.springframework.ide.vscode.commons.util.text.TextDocument;
2727

@@ -30,10 +30,10 @@
3030
*/
3131
public class WebfluxHandlerCodeLensProvider implements CodeLensProvider {
3232

33-
private final SpringSymbolIndex springIndexer;
33+
private final SpringMetamodelIndex springIndex;
3434

35-
public WebfluxHandlerCodeLensProvider(SpringSymbolIndex springIndexer) {
36-
this.springIndexer = springIndexer;
35+
public WebfluxHandlerCodeLensProvider(SpringMetamodelIndex springIndex) {
36+
this.springIndex = springIndex;
3737
}
3838

3939
@Override
@@ -59,21 +59,11 @@ protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node
5959
final String handlerMethod = methodBinding.getMethodDeclaration().toString().trim();
6060

6161
cancelToken.checkCanceled();
62-
63-
List<SymbolAddOnInformation> handlerInfos = this.springIndexer.getAllAdditionalInformation((addon) -> {
64-
if (addon instanceof WebfluxHandlerInformation) {
65-
WebfluxHandlerInformation handlerInfo = (WebfluxHandlerInformation) addon;
66-
return handlerInfo.getHandlerClass() != null && handlerInfo.getHandlerClass().equals(handlerClass)
67-
&& handlerInfo.getHandlerMethod() != null && handlerInfo.getHandlerMethod().equals(handlerMethod);
68-
}
69-
return false;
70-
});
71-
72-
if (handlerInfos != null && handlerInfos.size() > 0) {
73-
for (Object object : handlerInfos) {
62+
63+
List<WebfluxHandlerMethodIndexElement> matchingHandlerMethods = findMatchingHandlerMethogs(handlerClass, handlerMethod);
64+
if (matchingHandlerMethods.size() > 0) {
65+
for (WebfluxHandlerMethodIndexElement handlerInfo : matchingHandlerMethods) {
7466
try {
75-
WebfluxHandlerInformation handlerInfo = (WebfluxHandlerInformation) object;
76-
7767
CodeLens codeLens = new CodeLens();
7868
codeLens.setRange(document.toRange(node.getName().getStartPosition(), node.getName().getLength()));
7969

@@ -101,4 +91,16 @@ protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node
10191
}
10292
}
10393

94+
private List<WebfluxHandlerMethodIndexElement> findMatchingHandlerMethogs(String handlerClass, String handlerMethod) {
95+
Bean[] beans = springIndex.getBeans();
96+
97+
return Arrays.stream(beans)
98+
.flatMap(bean -> Arrays.stream(bean.getChildren()))
99+
.filter(element -> element instanceof WebfluxHandlerMethodIndexElement)
100+
.map(element -> (WebfluxHandlerMethodIndexElement) element)
101+
.filter(webfluxElement -> webfluxElement.getHandlerClass() != null && webfluxElement.getHandlerClass().equals(handlerClass)
102+
&& webfluxElement.getHandlerMethod() != null && webfluxElement.getHandlerMethod().equals(handlerMethod))
103+
.toList();
104+
}
105+
104106
}

0 commit comments

Comments
 (0)