Skip to content

Commit

Permalink
GH-1041: replaced webflux symbol addon information objects with new i…
Browse files Browse the repository at this point in the history
…ndex child nodes

some additional groundwork for nested structure inside of spring index, in support of GH-1425, GH-1431, GH-1424
  • Loading branch information
martinlippert committed Jan 15, 2025
1 parent c1b1a06 commit 21accae
Show file tree
Hide file tree
Showing 17 changed files with 481 additions and 349 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.protocol.spring;

public abstract class AbstractSpringIndexElement implements SpringIndexElement {

public static final SpringIndexElement[] NO_CHILDREN = new SpringIndexElement[0];

private final SpringIndexElement[] children;

public AbstractSpringIndexElement(SpringIndexElement[] children) {
this.children = children != null ? children : NO_CHILDREN;
}

@Override
public SpringIndexElement[] getChildren() {
return children;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import com.google.gson.Gson;

public class Bean {
public class Bean extends AbstractSpringIndexElement {

private final String name;
private final String type;
Expand All @@ -32,7 +32,11 @@ public Bean(
Location location,
InjectionPoint[] injectionPoints,
Set<String> supertypes,
AnnotationMetadata[] annotations, boolean isConfiguration) {
AnnotationMetadata[] annotations,
boolean isConfiguration,
SpringIndexElement[] children) {

super(children);

this.name = name;
this.type = type;
Expand Down Expand Up @@ -64,6 +68,18 @@ else if (supertypes != null && supertypes.size() == 1 && supertypes.contains("ja
}
}

public Bean(
String name,
String type,
Location location,
InjectionPoint[] injectionPoints,
Set<String> supertypes,
AnnotationMetadata[] annotations,
boolean isConfiguration) {
this(name, type, location, injectionPoints, supertypes, annotations, isConfiguration, AbstractSpringIndexElement.NO_CHILDREN);
}


public String getName() {
return name;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.protocol.spring;

public interface SpringIndexElement {

SpringIndexElement[] getChildren();

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.springframework.ide.vscode.boot.java.data.DataRepositorySymbolProvider;
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider;
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
import org.springframework.ide.vscode.boot.java.utils.RestrictedDefaultSymbolProvider;

@Configuration(proxyBeanMethods = false)
Expand All @@ -36,7 +35,6 @@ AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache)
ComponentSymbolProvider componentSymbolProvider = new ComponentSymbolProvider();
RestrictedDefaultSymbolProvider restrictedDefaultSymbolProvider = new RestrictedDefaultSymbolProvider();
DataRepositorySymbolProvider dataRepositorySymbolProvider = new DataRepositorySymbolProvider();
WebfluxRouterSymbolProvider webfluxRouterSymbolProvider = new WebfluxRouterSymbolProvider();

providers.put(Annotations.SPRING_REQUEST_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.SPRING_GET_MAPPING, requestMappingSymbolProvider);
Expand Down Expand Up @@ -72,7 +70,6 @@ AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache)
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, restrictedDefaultSymbolProvider);

providers.put(Annotations.REPOSITORY, dataRepositorySymbolProvider);
providers.put("", webfluxRouterSymbolProvider);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.util.UriUtil;

import com.google.common.collect.ImmutableMultimap;
Expand Down Expand Up @@ -374,6 +375,7 @@ public static Gson createGson() {
.registerTypeAdapter(Bean.class, new BeanJsonAdapter())
.registerTypeAdapter(InjectionPoint.class, new InjectionPointJsonAdapter())
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
.registerTypeAdapter(SpringIndexElement.class, new SpringIndexElementAdapter())
.create();
}

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


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

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

}

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

@Override
Expand All @@ -637,9 +637,6 @@ public DeltaStorage<?> deserialize(JsonElement json, Type type, JsonDeserializat
}
}

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

@Override
Expand All @@ -664,9 +661,6 @@ public SymbolAddOnInformation deserialize(JsonElement json, Type type, JsonDeser
}
}

/**
* gson adapter to store subtype information for beans
*/
private static class BeanJsonAdapter implements JsonDeserializer<Bean> {

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

JsonElement isConfigurationObject = parsedObject.get("isConfiguration");
boolean isConfiguration = context.deserialize(isConfigurationObject, boolean.class);

JsonElement childrenObject = parsedObject.get("children");
SpringIndexElement[] children = context.deserialize(childrenObject, SpringIndexElement[].class);

return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations, isConfiguration);
return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations, isConfiguration, children);
}
}

Expand All @@ -714,4 +711,27 @@ public InjectionPoint deserialize(JsonElement json, Type type, JsonDeserializati
}
}

private static class SpringIndexElementAdapter implements JsonSerializer<SpringIndexElement>, JsonDeserializer<SpringIndexElement> {

@Override
public JsonElement serialize(SpringIndexElement element, Type typeOfSrc, JsonSerializationContext context) {
JsonElement elem = context.serialize(element);
elem.getAsJsonObject().addProperty("type", element.getClass().getName());
return elem;
}

@Override
public SpringIndexElement deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String typeName = jsonObject.get("type").getAsString();

try {
return context.deserialize(jsonObject, (Class<?>) Class.forName(typeName));
} catch (ClassNotFoundException e) {
throw new JsonParseException(e);
}
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public BootJavaLanguageServerComponents(ApplicationContext appContext) {

spelSemanticTokens = appContext.getBean(SpelSemanticTokens.class);

codeLensHandler = createCodeLensEngine(springSymbolIndex, projectFinder, server, spelSemanticTokens);
codeLensHandler = createCodeLensEngine(springIndex, projectFinder, server, spelSemanticTokens);

highlightsEngine = createDocumentHighlightEngine(appContext);
documents.onDocumentHighlight(highlightsEngine);
Expand Down Expand Up @@ -328,17 +328,17 @@ protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server,
return new BootJavaReferencesHandler(this, cuCache, projectFinder, providers);
}

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

return new BootJavaCodeLensEngine(this, codeLensProvider);
}

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

Map<String, JdtAstDocHighlightsProvider> astHighlightProviders = appContext.getBeansOfType(JdtAstDocHighlightsProvider.class);
if (!astHighlightProviders.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
Expand All @@ -34,13 +35,16 @@
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
Expand All @@ -57,13 +61,12 @@
*/
public class BeansSymbolProvider extends AbstractSymbolProvider {


private static final Logger log = LoggerFactory.getLogger(BeansSymbolProvider.class);

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

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

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

if (isMethodAbstract(method)) return;

boolean isWebfluxRouter = WebfluxRouterSymbolProvider.isWebfluxRouterBean(method);

// for webflux details, we need full method body ASTs
if (isWebfluxRouter && SCAN_PASS.ONE.equals(context.getPass())) {
context.getNextPassFiles().add(context.getFile());
return;
}

List<SpringIndexElement> childElements = new ArrayList<>();

if (isWebfluxRouter) {
WebfluxRouterSymbolProvider.createWebfluxElements(method, context, doc, childElements);
}

boolean isFunction = isFunctionBean(method);

ITypeBinding beanType = getBeanType(method);
String markerString = getAnnotations(method);

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

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

context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2021 Pivotal, Inc.
* Copyright (c) 2018, 2024 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand All @@ -9,7 +9,7 @@
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.requestmapping;

import java.util.Arrays;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTVisitor;
Expand All @@ -19,9 +19,9 @@
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.text.TextDocument;

Expand All @@ -30,10 +30,10 @@
*/
public class WebfluxHandlerCodeLensProvider implements CodeLensProvider {

private final SpringSymbolIndex springIndexer;
private final SpringMetamodelIndex springIndex;

public WebfluxHandlerCodeLensProvider(SpringSymbolIndex springIndexer) {
this.springIndexer = springIndexer;
public WebfluxHandlerCodeLensProvider(SpringMetamodelIndex springIndex) {
this.springIndex = springIndex;
}

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

cancelToken.checkCanceled();

List<SymbolAddOnInformation> handlerInfos = this.springIndexer.getAllAdditionalInformation((addon) -> {
if (addon instanceof WebfluxHandlerInformation) {
WebfluxHandlerInformation handlerInfo = (WebfluxHandlerInformation) addon;
return handlerInfo.getHandlerClass() != null && handlerInfo.getHandlerClass().equals(handlerClass)
&& handlerInfo.getHandlerMethod() != null && handlerInfo.getHandlerMethod().equals(handlerMethod);
}
return false;
});

if (handlerInfos != null && handlerInfos.size() > 0) {
for (Object object : handlerInfos) {

List<WebfluxHandlerMethodIndexElement> matchingHandlerMethods = findMatchingHandlerMethogs(handlerClass, handlerMethod);
if (matchingHandlerMethods.size() > 0) {
for (WebfluxHandlerMethodIndexElement handlerInfo : matchingHandlerMethods) {
try {
WebfluxHandlerInformation handlerInfo = (WebfluxHandlerInformation) object;

CodeLens codeLens = new CodeLens();
codeLens.setRange(document.toRange(node.getName().getStartPosition(), node.getName().getLength()));

Expand Down Expand Up @@ -101,4 +91,16 @@ protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node
}
}

private List<WebfluxHandlerMethodIndexElement> findMatchingHandlerMethogs(String handlerClass, String handlerMethod) {
Bean[] beans = springIndex.getBeans();

return Arrays.stream(beans)
.flatMap(bean -> Arrays.stream(bean.getChildren()))
.filter(element -> element instanceof WebfluxHandlerMethodIndexElement)
.map(element -> (WebfluxHandlerMethodIndexElement) element)
.filter(webfluxElement -> webfluxElement.getHandlerClass() != null && webfluxElement.getHandlerClass().equals(handlerClass)
&& webfluxElement.getHandlerMethod() != null && webfluxElement.getHandlerMethod().equals(handlerMethod))
.toList();
}

}
Loading

0 comments on commit 21accae

Please sign in to comment.