Skip to content

Commit 61052cb

Browse files
authored
Open stereotype definition JSON (#1663)
* Open default stereotype definition JSON * Support fetching JAR url content via spring-boot-ls uri Signed-off-by: BoykoAlex <[email protected]>
1 parent 5723c3c commit 61052cb

File tree

5 files changed

+76
-50
lines changed

5 files changed

+76
-50
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.context.annotation.Bean;
1414
import org.springframework.context.annotation.Configuration;
1515
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
16+
import org.springframework.ide.vscode.boot.java.commands.Misc;
1617
import org.springframework.ide.vscode.boot.java.commands.SpringIndexCommands;
1718
import org.springframework.ide.vscode.boot.java.commands.WorkspaceBootExecutableProjects;
1819
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
@@ -31,6 +32,11 @@ public class CommandsConfig {
3132
SpringIndexCommands springIndexCommands(SimpleLanguageServer server, JavaProjectFinder projectFinder,
3233
SpringMetamodelIndex symbolIndex, ModulithService modulithService, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
3334
return new SpringIndexCommands(server, symbolIndex, modulithService, projectFinder, stereotypeCatalogRegistry);
34-
}
35-
35+
}
36+
37+
@Bean
38+
Misc misc(SimpleLanguageServer server) {
39+
return new Misc(server);
40+
}
41+
3642
}

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

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

1717
package org.springframework.ide.vscode.boot.java.commands;
1818

19-
import java.net.URI;
2019
import java.net.URISyntaxException;
2120
import java.net.URL;
2221
import java.util.ArrayList;
@@ -97,16 +96,13 @@ public void handleStereotype(Stereotype stereotype, NodeContext context) {
9796
for (Object source : sources) {
9897
if (source instanceof URL) {
9998
URL url = (URL) source;
100-
if (url.getProtocol().equals("jar")) {
101-
reference = convertUrlToJdtUri((URL) source, project.getElementName());
102-
}
103-
else if (url.getProtocol().equals("file")) {
104-
try {
105-
reference = url.toURI().toASCIIString();
106-
}
107-
catch (URISyntaxException e) {
108-
// something went wrong
99+
try {
100+
reference = url.toURI().toASCIIString();
101+
if (reference.startsWith(Misc.JAR_URL_PROTOCOL_PREFIX)) {
102+
reference = reference.replaceFirst(Misc.JAR_URL_PROTOCOL_PREFIX, Misc.BOOT_LS_URL_PRTOCOL_PREFIX);
109103
}
104+
} catch (URISyntaxException e) {
105+
// something went wrong
110106
}
111107
}
112108
else if (source instanceof Location) {
@@ -123,39 +119,7 @@ else if (source instanceof Location) {
123119
);
124120
}
125121
}
126-
127-
private String convertUrlToJdtUri(URL url, String projectName) {
128-
try {
129-
URI uri = url.toURI();
130-
131-
// Extract the scheme-specific part (everything after "jar:")
132-
String schemeSpecificPart = uri.getSchemeSpecificPart();
133-
134-
// Split on "!/" to separate jar file path from internal path
135-
String[] parts = schemeSpecificPart.split("!/", 2);
136-
137-
if (parts.length != 2) {
138-
return null;
139-
}
140-
141-
String jarFilePath = parts[0];
142-
String internalPath = parts[1];
143-
144-
// Remove "file:" prefix from jar file path if present
145-
if (jarFilePath.startsWith("file:")) {
146-
jarFilePath = jarFilePath.substring(5);
147-
}
148-
149-
String jarFileName = jarFilePath.substring(jarFilePath.lastIndexOf('/') + 1);
150-
151-
// Construct the JDT URI with just the jar file name
152-
return "jdt://contents/" + jarFileName + "/" + internalPath + "?=" + projectName;
153-
154-
} catch (URISyntaxException e) {
155-
return null;
156-
}
157-
}
158-
122+
159123
@Override
160124
public void handleApplication(A application) {
161125
this.root
Lines changed: 50 additions & 0 deletions
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.io.InputStream;
14+
import java.net.URI;
15+
import java.net.URLDecoder;
16+
import java.nio.charset.StandardCharsets;
17+
18+
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
19+
import org.springframework.ide.vscode.commons.util.IOUtil;
20+
21+
import com.google.gson.JsonElement;
22+
23+
24+
public class Misc {
25+
26+
public static final String BOOT_LS_URL_PRTOCOL_PREFIX = "spring-boot-ls:";
27+
public static final String JAR_URL_PROTOCOL_PREFIX = "jar:";
28+
29+
private static final String STS_FETCH_JAR_CONTENT = "sts/jar/fetch-content";
30+
31+
public Misc(SimpleLanguageServer server) {
32+
// Fetch JAR content via a special protocol `spring-boot-ls` as we don't want to handle all JARs in VSCode
33+
server.onCommand(STS_FETCH_JAR_CONTENT, params -> {
34+
return server.getAsync().invoke(() -> {
35+
if (params.getArguments().size() == 1) {
36+
Object o = params.getArguments().get(0);
37+
String s = o instanceof JsonElement ? ((JsonElement) o).getAsString() : o.toString();
38+
if (s.startsWith(BOOT_LS_URL_PRTOCOL_PREFIX)) {
39+
s = s.replaceFirst(BOOT_LS_URL_PRTOCOL_PREFIX, JAR_URL_PROTOCOL_PREFIX);
40+
URI uri = URI.create(URLDecoder.decode(s, StandardCharsets.UTF_8));
41+
// Java has support for JAR URLs
42+
return IOUtil.toString((InputStream) uri.toURL().getContent());
43+
}
44+
}
45+
throw new IllegalArgumentException("The command must have one valid URL parameter.");
46+
});
47+
});
48+
}
49+
50+
}

vscode-extensions/vscode-spring-boot/lib/Main.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
ExtensionContext,
99
Uri,
1010
lm,
11-
TreeItemCollapsibleState
11+
TreeItemCollapsibleState,
12+
TextDocumentContentProvider
1213
} from 'vscode';
1314

1415
import * as commons from '@pivotal-tools/commons-vscode';
@@ -191,6 +192,13 @@ export function activate(context: ExtensionContext): Thenable<ExtensionAPI> {
191192

192193
context.subscriptions.push(commands.registerCommand('vscode-spring-boot.agent.apply', applyLspEdit));
193194

195+
// Register content loader for URIs of type `spring-boot-ls:...` (load JAR content via Boot LS)
196+
context.subscriptions.push(workspace.registerTextDocumentContentProvider('spring-boot-ls', new (class implements TextDocumentContentProvider {
197+
provideTextDocumentContent(uri: Uri) {
198+
return commands.executeCommand<string>('sts/jar/fetch-content', uri.toString());
199+
}
200+
})()));
201+
194202
const api = new ApiManager(client).api
195203

196204
context.subscriptions.push(api.getSpringIndex().onSpringIndexUpdated(e => structureManager.refresh(false)));

vscode-extensions/vscode-spring-boot/lib/explorer/structure-tree-manager.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { commands, EventEmitter, Event, ExtensionContext, Disposable, window, TreeItemCollapsibleState, TreeItem, QuickPickItem, QuickPickOptions, Memento, workspace } from "vscode";
1+
import { commands, EventEmitter, Event, ExtensionContext, window, Memento, Uri, QuickPickItem } from "vscode";
22
import { SpringNode, StereotypedNode } from "./nodes";
3-
import { ExplorerTreeProvider } from "./explorer-tree-provider";
43

54
const SPRING_STRUCTURE_CMD = "sts/spring-boot/structure";
65

@@ -17,8 +16,7 @@ export class StructureManager {
1716
if (node && node.getReferenceValue) {
1817
const reference = node.getReferenceValue();
1918
if (reference) {
20-
// Reference is a specific URL that should be passed to java.open.file command
21-
commands.executeCommand('java.open.file', reference);
19+
commands.executeCommand('vscode.open', Uri.parse(reference));
2220
}
2321
}
2422
}));

0 commit comments

Comments
 (0)