Skip to content

Commit 23afff6

Browse files
authored
Support exporting jar (#271)
1 parent 63f173a commit 23afff6

24 files changed

+1211
-35
lines changed

jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<command id="java.project.list"/>
88
<command id="java.getPackageData"/>
99
<command id="java.resolvePath" />
10+
<command id="java.project.getMainMethod" />
11+
<command id="java.project.generateJar" />
1012
</delegateCommandHandler>
1113
</extension>
1214
<extension

jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
3131
return PackageCommand.getChildren(arguments, monitor);
3232
case "java.resolvePath":
3333
return PackageCommand.resolvePath(arguments, monitor);
34+
case "java.project.getMainMethod":
35+
return ProjectCommand.getMainMethod(arguments, monitor);
36+
case "java.project.generateJar":
37+
return ProjectCommand.exportJar(arguments, monitor);
3438
default:
3539
break;
3640
}

jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ private static Object[] findJarDirectoryChildren(JarEntryDirectory directory, St
526526
return null;
527527
}
528528

529-
private static IJavaProject getJavaProject(String projectUri) {
529+
public static IJavaProject getJavaProject(String projectUri) {
530530
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
531531
IContainer[] containers = root.findContainersForLocationURI(JDTUtils.toURI(projectUri));
532532

jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java

+162
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,45 @@
1111

1212
package com.microsoft.jdtls.ext.core;
1313

14+
import java.io.File;
15+
import java.io.FileOutputStream;
1416
import java.util.ArrayList;
1517
import java.util.Arrays;
18+
import java.util.HashSet;
1619
import java.util.List;
1720
import java.util.Objects;
21+
import java.util.Set;
22+
import java.util.jar.Attributes;
23+
import java.util.jar.JarOutputStream;
24+
import java.util.jar.Manifest;
25+
import java.util.zip.ZipFile;
1826

27+
import com.google.gson.Gson;
28+
import com.google.gson.GsonBuilder;
1929
import org.eclipse.core.resources.IProject;
2030
import org.eclipse.core.resources.IWorkspaceRoot;
2131
import org.eclipse.core.resources.ResourcesPlugin;
32+
import org.eclipse.core.runtime.CoreException;
2233
import org.eclipse.core.runtime.IPath;
2334
import org.eclipse.core.runtime.IProgressMonitor;
35+
import org.eclipse.core.runtime.NullProgressMonitor;
36+
import org.eclipse.core.runtime.Path;
37+
import org.eclipse.core.resources.IFile;
38+
import org.eclipse.jdt.core.search.IJavaSearchScope;
39+
import org.eclipse.jdt.core.search.SearchEngine;
40+
import org.eclipse.jdt.core.search.SearchPattern;
41+
import org.eclipse.jdt.core.IJavaElement;
42+
import org.eclipse.jdt.core.IJavaProject;
43+
import org.eclipse.jdt.core.IMethod;
44+
import org.eclipse.jdt.core.IModuleDescription;
45+
import org.eclipse.jdt.core.IPackageFragmentRoot;
2446
import org.eclipse.jdt.core.JavaCore;
47+
import org.eclipse.jdt.core.JavaModelException;
48+
import org.eclipse.jdt.core.search.IJavaSearchConstants;
49+
import org.eclipse.jdt.core.search.SearchRequestor;
50+
import org.eclipse.jdt.core.search.SearchMatch;
51+
import org.eclipse.jdt.core.search.SearchParticipant;
52+
import org.eclipse.jdt.launching.JavaRuntime;
2553
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
2654
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
2755
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
@@ -30,9 +58,31 @@
3058

3159
import com.microsoft.jdtls.ext.core.model.NodeKind;
3260
import com.microsoft.jdtls.ext.core.model.PackageNode;
61+
import org.eclipse.lsp4j.jsonrpc.json.adapters.CollectionTypeAdapter;
62+
import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapter;
63+
64+
import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.writeFile;
65+
import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.writeArchive;
3366

3467
public final class ProjectCommand {
3568

69+
public static class MainClassInfo {
70+
71+
public String name;
72+
73+
public String path;
74+
75+
public MainClassInfo(String name, String path) {
76+
this.name = name;
77+
this.path = path;
78+
}
79+
}
80+
81+
private static final Gson gson = new GsonBuilder()
82+
.registerTypeAdapterFactory(new CollectionTypeAdapter.Factory())
83+
.registerTypeAdapterFactory(new EnumTypeAdapter.Factory())
84+
.create();
85+
3686
public static List<PackageNode> listProjects(List<Object> arguments, IProgressMonitor monitor) {
3787
String workspaceUri = (String) arguments.get(0);
3888
IPath workspacePath = ResourceUtils.canonicalFilePathFromURI(workspaceUri);
@@ -79,4 +129,116 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) {
79129
String fileName = workspacePath.toFile().getName();
80130
return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode());
81131
}
132+
133+
public static boolean exportJar(List<Object> arguments, IProgressMonitor monitor) {
134+
if (arguments.size() < 3) {
135+
return false;
136+
}
137+
String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
138+
String[] classpaths = gson.fromJson(gson.toJson(arguments.get(1)), String[].class);
139+
String destination = gson.fromJson(gson.toJson(arguments.get(2)), String.class);
140+
Manifest manifest = new Manifest();
141+
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
142+
if (mainMethod.length() > 0) {
143+
manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,mainMethod);
144+
}
145+
try (JarOutputStream target = new JarOutputStream(new FileOutputStream(destination), manifest)) {
146+
Set<String> fDirectories = new HashSet<>();
147+
for (String classpath : classpaths) {
148+
if (classpath != null) {
149+
if(classpath.endsWith(".jar")) {
150+
ZipFile zip = new ZipFile(classpath);
151+
writeArchive(zip, true, true, target, fDirectories, monitor);
152+
}
153+
else {
154+
File folder = new File(classpath);
155+
writeFileRecursively(folder, target, fDirectories, folder.getAbsolutePath().length() + 1);
156+
}
157+
}
158+
}
159+
} catch (Exception e) {
160+
return false;
161+
}
162+
return true;
163+
}
164+
165+
private static void writeFileRecursively(File folder, JarOutputStream fJarOutputStream, Set<String> fDirectories, int len) {
166+
File[] files = folder.listFiles();
167+
for (File file : files) {
168+
if (file.isDirectory()) {
169+
writeFileRecursively(file, fJarOutputStream, fDirectories, len);
170+
} else if (file.isFile()) {
171+
try {
172+
writeFile(file, new Path(file.getAbsolutePath().substring(len)), true, true, fJarOutputStream, fDirectories);
173+
}
174+
catch (Exception e) {
175+
// do nothing
176+
}
177+
}
178+
}
179+
}
180+
181+
public static List<MainClassInfo> getMainMethod(List<Object> arguments, IProgressMonitor monitor) throws Exception {
182+
List<PackageNode> projectList = listProjects(arguments, monitor);
183+
final List<MainClassInfo> res = new ArrayList<>();
184+
List<IJavaElement> searchRoots = new ArrayList<>();
185+
if (projectList.size() == 0) {
186+
return res;
187+
}
188+
for (PackageNode project : projectList) {
189+
IJavaProject javaProject = PackageCommand.getJavaProject(project.getUri());
190+
for (IPackageFragmentRoot packageFragmentRoot : javaProject.getAllPackageFragmentRoots()) {
191+
if (!packageFragmentRoot.isExternal()) {
192+
searchRoots.add(packageFragmentRoot);
193+
}
194+
}
195+
}
196+
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(searchRoots.toArray(IJavaElement[]::new));
197+
SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD,
198+
IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
199+
SearchRequestor requestor = new SearchRequestor() {
200+
@Override
201+
public void acceptSearchMatch(SearchMatch match) {
202+
Object element = match.getElement();
203+
if (!(element instanceof IMethod)) {
204+
return;
205+
}
206+
IMethod method = (IMethod) element;
207+
try {
208+
if (!method.isMainMethod() || method.getResource() == null || method.getJavaProject() == null) {
209+
return;
210+
}
211+
String mainClass = method.getDeclaringType().getFullyQualifiedName();
212+
String filePath = "";
213+
if (match.getResource() instanceof IFile) {
214+
filePath = match.getResource().getLocation().toOSString();
215+
}
216+
res.add(new MainClassInfo(mainClass, filePath));
217+
} catch (JavaModelException e) {
218+
// ignore
219+
}
220+
}
221+
};
222+
SearchEngine searchEngine = new SearchEngine();
223+
try {
224+
searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()},
225+
scope, requestor, new NullProgressMonitor());
226+
} catch (CoreException e) {
227+
// ignore
228+
}
229+
return res;
230+
}
231+
232+
public static String getModuleName(IJavaProject project) {
233+
if (project == null || !JavaRuntime.isModularProject(project)) {
234+
return null;
235+
}
236+
try {
237+
IModuleDescription module = project.getModuleDescription();
238+
return module == null ? null : module.getElementName();
239+
} catch (CoreException e) {
240+
return null;
241+
}
242+
}
243+
82244
}

0 commit comments

Comments
 (0)