Skip to content

Commit f03cb8c

Browse files
committed
initial support for modulith inside of new structure view
1 parent 0baf453 commit f03cb8c

21 files changed

+965
-201
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.springframework.ide.vscode.boot.java.commands.SpringIndexCommands;
1717
import org.springframework.ide.vscode.boot.java.commands.WorkspaceBootExecutableProjects;
1818
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
19+
import org.springframework.ide.vscode.boot.modulith.ModulithService;
1920
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
2021
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
2122

@@ -28,8 +29,8 @@ public class CommandsConfig {
2829

2930
@Bean
3031
SpringIndexCommands springIndexCommands(SimpleLanguageServer server, JavaProjectFinder projectFinder,
31-
SpringMetamodelIndex symbolIndex, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
32-
return new SpringIndexCommands(server, symbolIndex, projectFinder, stereotypeCatalogRegistry);
32+
SpringMetamodelIndex symbolIndex, ModulithService modulithService, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
33+
return new SpringIndexCommands(server, symbolIndex, modulithService, projectFinder, stereotypeCatalogRegistry);
3334
}
3435

3536
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.commands;
12+
13+
import java.util.Collection;
14+
15+
import org.springframework.ide.vscode.boot.modulith.AppModule;
16+
import org.springframework.ide.vscode.boot.modulith.NamedInterface;
17+
import org.springframework.util.StringUtils;
18+
19+
public class ApplicationModule {
20+
21+
private final AppModule appModule;
22+
23+
public ApplicationModule(AppModule appModule) {
24+
this.appModule = appModule;
25+
}
26+
27+
public String getDisplayName() {
28+
return appModule.displayName() != null ? appModule.displayName() :
29+
StringUtils.capitalize(lastSegment(appModule.basePackage()));
30+
}
31+
32+
public String getBasePackage() {
33+
return appModule.basePackage();
34+
}
35+
36+
private String lastSegment(String packageName) {
37+
int i = packageName.lastIndexOf('.');
38+
if (i >= 0) {
39+
return packageName.substring(i);
40+
}
41+
else {
42+
return packageName;
43+
}
44+
}
45+
46+
public Collection<NamedInterface> getNamedInterfaces() {
47+
return appModule.namedInterfaces();
48+
}
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.commands;
12+
13+
import java.util.Optional;
14+
import java.util.stream.Stream;
15+
16+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeClassElement;
17+
import org.springframework.ide.vscode.boot.modulith.AppModule;
18+
import org.springframework.ide.vscode.boot.modulith.AppModules;
19+
20+
public class ApplicationModules {
21+
22+
private final AppModules modules;
23+
24+
public ApplicationModules(AppModules modules) {
25+
this.modules = modules;
26+
}
27+
28+
public Optional<String> getSystemName() {
29+
return Optional.empty();
30+
}
31+
32+
public Optional<ApplicationModule> getModuleForPackage(String name) {
33+
Optional<AppModule> module = modules.getModuleForPackage(name);
34+
if (module.isPresent()) {
35+
return Optional.of(new ApplicationModule(module.get()));
36+
}
37+
return Optional.empty();
38+
}
39+
40+
public Optional<ApplicationModule> getModuleByType(StereotypeClassElement type) {
41+
return Optional.empty();
42+
}
43+
44+
public Stream<ApplicationModule> stream() {
45+
return modules.stream().map(module -> new ApplicationModule(module));
46+
}
47+
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ide.vscode.boot.java.commands;
18+
19+
import java.util.Optional;
20+
import java.util.function.BiConsumer;
21+
import java.util.function.Consumer;
22+
23+
import org.jmolecules.stereotype.api.Stereotype;
24+
import org.jmolecules.stereotype.tooling.LabelProvider;
25+
import org.jmolecules.stereotype.tooling.MethodNodeContext;
26+
import org.jmolecules.stereotype.tooling.NodeContext;
27+
import org.jmolecules.stereotype.tooling.NodeHandler;
28+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeClassElement;
29+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeMethodElement;
30+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypePackageElement;
31+
32+
import com.google.gson.Gson;
33+
import com.google.gson.GsonBuilder;
34+
35+
/**
36+
* @author Oliver Drotbohm
37+
* @author Martin Lippert
38+
*/
39+
public class ApplicationModulesJsonNodeHandler implements NodeHandler<ApplicationModules, StereotypePackageElement, StereotypeClassElement, StereotypeMethodElement, NamedInterfaceNode> {
40+
41+
private static final String LOCATION = "location";
42+
static final String ICON = "icon";
43+
static final String TEXT = "text";
44+
45+
private final Node root;
46+
private final LabelProvider<ApplicationModules, StereotypePackageElement, StereotypeClassElement, StereotypeMethodElement, NamedInterfaceNode> labels;
47+
private final BiConsumer<Node, NamedInterfaceNode> customHandler;
48+
private Node current;
49+
50+
public ApplicationModulesJsonNodeHandler(LabelProvider<ApplicationModules, StereotypePackageElement, StereotypeClassElement, StereotypeMethodElement, NamedInterfaceNode> labels, BiConsumer<Node, NamedInterfaceNode> customHandler) {
51+
this.labels = labels;
52+
this.root = new Node(null);
53+
this.customHandler = customHandler;
54+
this.current = root;
55+
}
56+
57+
@Override
58+
public void handleStereotype(Stereotype stereotype, NodeContext context) {
59+
60+
// icon for concrete stereotype
61+
String stereotypeID = stereotype.getIdentifier();
62+
63+
String icon = StereotypeIcons.ICONS.get(stereotypeID);
64+
65+
// group fallback
66+
if (icon == null) {
67+
Optional<String> groupIcon = stereotype.getGroups().stream()
68+
.filter(group -> StereotypeIcons.ICONS.containsKey(group))
69+
.map(group -> StereotypeIcons.ICONS.get(group))
70+
.findFirst();
71+
72+
icon = groupIcon.isPresent() ? icon = groupIcon.get() : StereotypeIcons.ICONS.get("Stereotype");
73+
}
74+
75+
String finalIcon = icon;
76+
addChild(node -> node
77+
.withAttribute(TEXT, labels.getSterotypeLabel(stereotype))
78+
.withAttribute(ICON, finalIcon)
79+
);
80+
}
81+
82+
@Override
83+
public void handleApplication(ApplicationModules application) {
84+
this.root
85+
.withAttribute(TEXT, labels.getApplicationLabel(application))
86+
.withAttribute(ICON, StereotypeIcons.ICONS.get("Application"))
87+
;
88+
}
89+
90+
@Override
91+
public void handlePackage(StereotypePackageElement pkg, NodeContext context) {
92+
93+
addChild(node -> node
94+
.withAttribute(TEXT, labels.getPackageLabel(pkg))
95+
.withAttribute(ICON, StereotypeIcons.ICONS.get("Packages"))
96+
);
97+
}
98+
99+
@Override
100+
public void handleType(StereotypeClassElement type, NodeContext context) {
101+
addChild(node -> node
102+
.withAttribute(TEXT, labels.getTypeLabel(type))
103+
.withAttribute(LOCATION, type.getLocation())
104+
.withAttribute(ICON, StereotypeIcons.ICONS.get("Type"))
105+
);
106+
}
107+
108+
@Override
109+
public void handleMethod(StereotypeMethodElement method, MethodNodeContext<StereotypeClassElement> context) {
110+
addChildFoo(node -> node
111+
.withAttribute(TEXT, labels.getMethodLabel(method, context.getContextualType()))
112+
.withAttribute(LOCATION, method.getLocation())
113+
.withAttribute(ICON, StereotypeIcons.ICONS.get("Method"))
114+
);
115+
}
116+
117+
@Override
118+
public void handleCustom(NamedInterfaceNode custom, NodeContext context) {
119+
addChild(node -> customHandler.accept(node, custom));
120+
}
121+
122+
public Node createNested() {
123+
return new Node(this.current);
124+
}
125+
126+
@Override
127+
public void postGroup() {
128+
this.current = this.current.parent;
129+
}
130+
131+
private void addChild(Consumer<Node> consumer) {
132+
this.current = addChildFoo(consumer);
133+
}
134+
135+
private Node addChildFoo(Consumer<Node> consumer) {
136+
137+
var node = new Node(this.current);
138+
consumer.accept(node);
139+
140+
this.current.children.add(node);
141+
142+
return node;
143+
}
144+
145+
@Override
146+
public String toString() {
147+
Gson gson = new GsonBuilder().setPrettyPrinting().create();
148+
return gson.toJson(root);
149+
}
150+
151+
Node getRoot() {
152+
return root;
153+
}
154+
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.commands;
12+
13+
import java.util.stream.Collectors;
14+
15+
import org.jmolecules.stereotype.api.Stereotype;
16+
import org.jmolecules.stereotype.catalog.StereotypeCatalog;
17+
import org.jmolecules.stereotype.catalog.StereotypeGroup;
18+
import org.jmolecules.stereotype.tooling.LabelProvider;
19+
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
20+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeClassElement;
21+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeMethodElement;
22+
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypePackageElement;
23+
import org.springframework.ide.vscode.commons.java.IJavaProject;
24+
25+
public class ApplicationModulesLabelProvider implements
26+
LabelProvider<ApplicationModules, StereotypePackageElement, StereotypeClassElement, StereotypeMethodElement, NamedInterfaceNode> {
27+
28+
private final StereotypeCatalog catalog;
29+
private final IJavaProject project;
30+
private final SpringMetamodelIndex springIndex;
31+
private final ApplicationModules modules;
32+
33+
public ApplicationModulesLabelProvider(StereotypeCatalog catalog, IJavaProject project, SpringMetamodelIndex springIndex, ApplicationModules modules) {
34+
this.catalog = catalog;
35+
this.project = project;
36+
this.springIndex = springIndex;
37+
this.modules = modules;
38+
}
39+
40+
@Override
41+
public String getApplicationLabel(ApplicationModules application) {
42+
return modules.getSystemName().orElse("Application");
43+
}
44+
45+
@Override
46+
public String getPackageLabel(StereotypePackageElement pkg) {
47+
var name = pkg.getPackageName();
48+
return modules.getModuleForPackage(name).map(ApplicationModule::getDisplayName).orElse(name);
49+
}
50+
51+
@Override
52+
public String getTypeLabel(StereotypeClassElement type) {
53+
54+
return type.getType();
55+
56+
// TODO: abbreviate with module name
57+
}
58+
59+
@Override
60+
public String getMethodLabel(StereotypeMethodElement method, StereotypeClassElement contextual) {
61+
return StructureViewUtil.getMethodLabel(project, springIndex, method, contextual);
62+
}
63+
64+
@Override
65+
public String getCustomLabel(NamedInterfaceNode ni) {
66+
return ni.toString();
67+
}
68+
69+
@Override
70+
public String getSterotypeLabel(Stereotype stereotype) {
71+
72+
var groups = catalog.getGroupsFor(stereotype);
73+
74+
return stereotype.getDisplayName() + (groups.isEmpty() ? ""
75+
: " " + groups.stream().map(StereotypeGroup::getDisplayName)
76+
.collect(Collectors.joining(", ", "(", ")")));
77+
}
78+
}

0 commit comments

Comments
 (0)