Skip to content

Commit 126a6c2

Browse files
committed
Merge branch 'jmolecules-support'
2 parents bdd303c + f074dce commit 126a6c2

File tree

62 files changed

+2609
-334
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2609
-334
lines changed

headless-services/spring-boot-language-server/pom.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@
5050
<enabled>false</enabled>
5151
</releases>
5252
</repository>
53+
54+
<repository>
55+
<id>jmolecules-snapshots</id>
56+
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
57+
<snapshots>
58+
<enabled>true</enabled>
59+
</snapshots>
60+
<releases>
61+
<enabled>false</enabled>
62+
</releases>
63+
</repository>
64+
5365
</repositories>
5466

5567
<dependencies>
@@ -97,6 +109,10 @@
97109
<artifactId>org.json</artifactId>
98110
<version>1.0.0-SNAPSHOT</version>
99111
</dependency>
112+
<dependency>
113+
<groupId>com.jayway.jsonpath</groupId>
114+
<artifactId>json-path</artifactId>
115+
</dependency>
100116
<dependency>
101117
<groupId>org.springframework.ide.vscode</groupId>
102118
<artifactId>java-properties</artifactId>
@@ -122,6 +138,7 @@
122138
<artifactId>commons-rewrite</artifactId>
123139
<version>${dependencies.version}</version>
124140
</dependency>
141+
125142
<dependency>
126143
<groupId>org.eclipse.jdt</groupId>
127144
<artifactId>org.eclipse.jdt.core</artifactId>
@@ -146,6 +163,12 @@
146163
<groupId>org.apache.commons</groupId>
147164
<artifactId>commons-text</artifactId>
148165
</dependency>
166+
167+
<dependency>
168+
<groupId>org.jmolecules.integrations</groupId>
169+
<artifactId>jmolecules-stereotype</artifactId>
170+
<version>0.29.0-STEREOTYPE-SNAPSHOT</version>
171+
</dependency>
149172

150173

151174
<dependency>

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
7979
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
8080
import org.springframework.ide.vscode.boot.java.spel.SpelDefinitionProvider;
81+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
8182
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
8283
import org.springframework.ide.vscode.boot.java.value.ValueDefinitionProvider;
8384
import org.springframework.ide.vscode.boot.jdt.ls.JavaProjectsService;
@@ -440,6 +441,11 @@ DataRepositoryAotMetadataService dataAotMetadataService() {
440441
return new DataRepositoryAotMetadataService();
441442
}
442443

444+
@Bean
445+
StereotypeCatalogRegistry stereotypeCatalogRegistry() {
446+
return new StereotypeCatalogRegistry();
447+
}
448+
443449
@Bean ResponseModifier responseModifier() {
444450
return new ResponseModifier();
445451
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
1616
import org.springframework.ide.vscode.boot.java.commands.SpringIndexCommands;
1717
import org.springframework.ide.vscode.boot.java.commands.WorkspaceBootExecutableProjects;
18+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
1819
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
1920
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
2021

@@ -25,9 +26,10 @@ public class CommandsConfig {
2526
return new WorkspaceBootExecutableProjects(server, projectFinder, symbolIndex);
2627
}
2728

28-
@Bean SpringIndexCommands springIndexCommands(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringMetamodelIndex symbolIndex) {
29-
return new SpringIndexCommands(server, symbolIndex, projectFinder);
30-
}
31-
29+
@Bean
30+
SpringIndexCommands springIndexCommands(SimpleLanguageServer server, JavaProjectFinder projectFinder,
31+
SpringMetamodelIndex symbolIndex, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
32+
return new SpringIndexCommands(server, symbolIndex, projectFinder, stereotypeCatalogRegistry);
33+
}
3234

3335
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@
2525
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
2626
import org.springframework.ide.vscode.boot.java.requestmapping.HttpExchangeSymbolProvider;
2727
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider;
28+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
29+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypesIndexer;
2830
import org.springframework.ide.vscode.boot.java.utils.RestrictedDefaultSymbolProvider;
2931

3032
@Configuration(proxyBeanMethods = false)
3133
public class SpringSymbolIndexerConfig {
3234

3335
@Bean
34-
AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache, DataRepositoryAotMetadataService repositoryMetadataService) {
36+
AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache, DataRepositoryAotMetadataService repositoryMetadataService, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
3537
AnnotationHierarchyAwareLookup<SymbolProvider> providers = new AnnotationHierarchyAwareLookup<>();
3638

3739
RequestMappingSymbolProvider requestMappingSymbolProvider = new RequestMappingSymbolProvider();
@@ -82,6 +84,8 @@ AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache,
8284

8385
providers.put(Annotations.FEIGN_CLIENT, new FeignClientSymbolProvider());
8486
providers.put(Annotations.HTTP_EXCHANGE, new HttpExchangeSymbolProvider());
87+
88+
providers.put(Annotations.JMOLECULES_STEREOTYPE, new StereotypesIndexer(stereotypeCatalogRegistry));
8589

8690
return providers;
8791
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public <T extends SpringIndexElement> List<T> getNodesOfType(Class<T> type) {
8181
return getNodesOfType(type, rootNodes);
8282
}
8383

84+
public <T extends SpringIndexElement> List<T> getNodesOfType(String projectName, Class<T> type) {
85+
ProjectElement project = this.projectRootElements.get(projectName);
86+
return project == null ? List.of() : getNodesOfType(type, List.of(project));
87+
}
88+
8489
public Bean[] getBeans() {
8590
List<SpringIndexElement> rootNodes = new ArrayList<SpringIndexElement>(this.projectRootElements.values());
8691
return getNodesOfType(Bean.class, rootNodes).toArray(Bean[]::new);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ public class Annotations {
130130
Annotations.RESOURCE_JAKARTA, Annotations.INJECT_JAKARTA, Annotations.NAMED_JAKARTA,
131131
Annotations.RESOURCE_JAVAX, Annotations.INJECT_JAVAX, Annotations.NAMED_JAVAX
132132
);
133-
133+
134+
public static final String JMOLECULES_STEREOTYPE = "org.jmolecules.stereotype.Stereotype";
134135

135136
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,144 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
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, Inc. - initial API and implementation
10+
*******************************************************************************/
111
package org.springframework.ide.vscode.boot.java.commands;
212

13+
import java.util.List;
14+
import java.util.Objects;
15+
import java.util.Optional;
16+
import java.util.function.BiConsumer;
17+
import java.util.stream.Collectors;
18+
19+
import org.jmolecules.stereotype.tooling.HierarchicalNodeHandler;
20+
import org.jmolecules.stereotype.tooling.LabelUtils;
21+
import org.jmolecules.stereotype.tooling.ProjectTree;
22+
import org.jmolecules.stereotype.tooling.SimpleLabelProvider;
323
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
24+
import org.springframework.ide.vscode.boot.java.Annotations;
25+
import org.springframework.ide.vscode.boot.java.commands.ToolsJsonNodeHandler.Node;
26+
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingIndexElement;
27+
import org.springframework.ide.vscode.boot.java.stereotypes.IndexBasedStereotypeFactory;
28+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
29+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeClassElement;
30+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeMethodElement;
31+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypePackageElement;
32+
import org.springframework.ide.vscode.boot.modulith.ModulithService;
33+
import org.springframework.ide.vscode.commons.java.IJavaProject;
434
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
535
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
636

737
public class SpringIndexCommands {
838

939
private static final String SPRING_STRUCTURE_CMD = "sts/spring-boot/structure";
1040

11-
public SpringIndexCommands(SimpleLanguageServer server, SpringMetamodelIndex metamodelIndex, JavaProjectFinder projectFinder) {
12-
server.onCommand(SPRING_STRUCTURE_CMD, params -> server.getAsync().invoke(() -> metamodelIndex.getProjects()));
41+
private final SpringMetamodelIndex springIndex;
42+
43+
public SpringIndexCommands(SimpleLanguageServer server, SpringMetamodelIndex springIndex, JavaProjectFinder projectFinder, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
44+
this.springIndex = springIndex;
45+
46+
server.onCommand(SPRING_STRUCTURE_CMD, params -> server.getAsync().invoke(() -> {
47+
return projectFinder.all().stream().map(project -> nodeFrom(stereotypeCatalogRegistry, springIndex, project)).filter(Objects::nonNull).collect(Collectors.toList());
48+
}));
49+
}
50+
51+
private Node nodeFrom(StereotypeCatalogRegistry stereotypeCatalogRegistry, SpringMetamodelIndex springIndex, IJavaProject project) {
52+
var catalog = stereotypeCatalogRegistry.getCatalogOf(project);
53+
var factory = new IndexBasedStereotypeFactory(catalog, springIndex);
54+
factory.registerStereotypeDefinitions();
55+
56+
StereotypePackageElement mainApplicationPackage = identifyMainApplicationPackage(project, springIndex);
57+
58+
var labelProvider = new SimpleLabelProvider<>(StereotypePackageElement::getPackageName, StereotypePackageElement::getPackageName, StereotypeClassElement::getType,
59+
(StereotypeMethodElement m, StereotypeClassElement __) -> m.getMethodName(), Object::toString)
60+
.withTypeLabel(it -> abbreviate(mainApplicationPackage, it))
61+
.withMethodLabel((m, c) -> getMethodLabel(project, m, c));
62+
63+
var structureProvider = new ToolsStructureProvider(springIndex, project);
64+
65+
// json output
66+
BiConsumer<Node, Object> consumer = (node, c) -> {
67+
node.withAttribute(HierarchicalNodeHandler.TEXT, labelProvider.getCustomLabel(c))
68+
.withAttribute(ToolsJsonNodeHandler.ICON, "fa-named-interface");
69+
};
70+
71+
var jsonHandler = new ToolsJsonNodeHandler(labelProvider, consumer);
72+
73+
var jsonTree = new ProjectTree<>(factory, catalog, jsonHandler)
74+
.withStructureProvider(structureProvider)
75+
.withGrouper("org.jmolecules.architecture")
76+
.withGrouper("org.jmolecules.ddd", "org.jmolecules.event", "spring", "jpa", "java");
77+
78+
jsonTree.process(mainApplicationPackage);
79+
80+
return jsonHandler.getRoot();
1381
}
1482

83+
private String abbreviate(StereotypePackageElement mainApplicationPackage, StereotypeClassElement it) {
84+
if (mainApplicationPackage == null || mainApplicationPackage.getPackageName() == null || mainApplicationPackage.getPackageName().isBlank()) {
85+
return it.getType();
86+
}
87+
else {
88+
return LabelUtils.abbreviate(it.getType(), mainApplicationPackage.getPackageName());
89+
}
90+
}
91+
92+
private String getMethodLabel(IJavaProject project, StereotypeMethodElement method, StereotypeClassElement clazz) {
93+
// TODO: special treatment for methods that have specific index elements with specific labels (e.g. mapping methods)
94+
95+
Optional<RequestMappingIndexElement> mapping = springIndex.getNodesOfType(project.getElementName(), RequestMappingIndexElement.class).stream()
96+
.filter(mappingElement -> mappingElement.getMethodSignature() != null && mappingElement.getMethodSignature().equals(method.getMethodSignature()))
97+
.findAny();
98+
99+
if (mapping.isPresent()) {
100+
return mapping.get().getDocumentSymbol().getName();
101+
}
102+
else {
103+
return method.getMethodLabel();
104+
}
105+
106+
}
107+
108+
public StereotypePackageElement identifyMainApplicationPackage(IJavaProject project, SpringMetamodelIndex springIndex) {
109+
List<StereotypeClassElement> classNodes = springIndex.getNodesOfType(project.getElementName(), StereotypeClassElement.class);
110+
111+
Optional<StereotypePackageElement> packageElement = classNodes.stream()
112+
.filter(node -> node.getAnnotationTypes().contains(Annotations.BOOT_APP))
113+
.map(node -> getPackage(node.getType()))
114+
.map(packageName -> findPackageNode(packageName, project, springIndex))
115+
.findFirst();
116+
117+
if (packageElement.isPresent()) {
118+
return packageElement.get();
119+
}
120+
else {
121+
return new StereotypePackageElement("", null);
122+
}
123+
}
124+
125+
private String getPackage(String fullyQualifiedClassName) {
126+
return ModulithService.getPackageNameFromTypeFQName(fullyQualifiedClassName);
127+
}
128+
129+
private StereotypePackageElement findPackageNode(String packageName, IJavaProject project, SpringMetamodelIndex springIndex) {
130+
List<StereotypePackageElement> packageNodes = springIndex.getNodesOfType(project.getElementName(), StereotypePackageElement.class);
131+
132+
Optional<StereotypePackageElement> found = packageNodes.stream()
133+
.filter(packageNode -> packageNode.getPackageName().equals(packageName))
134+
.findAny();
135+
136+
if (found.isPresent()) {
137+
return found.get();
138+
}
139+
else {
140+
return new StereotypePackageElement(packageName, null);
141+
}
142+
}
143+
15144
}

0 commit comments

Comments
 (0)