Skip to content

Commit b3a2e74

Browse files
Add a utility to resolve main methods from a java file (#222)
* Add an utility to resolve main methods from a java file Signed-off-by: Jinbo Wang <[email protected]> * Refactor the logic to search main method * Return DebugException instead of Exception * fix review comments * Rename codeLens to mainMethod info
1 parent 69b7c49 commit b3a2e74

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

com.microsoft.java.debug.plugin/plugin.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
<command
1313
id="vscode.java.validateLaunchConfig">
1414
</command>
15+
<command
16+
id="vscode.java.resolveMainMethod">
17+
</command>
1518
</delegateCommandHandler>
1619
</extension>
1720
</plugin>

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler
3434

3535
public static String VALIDATE_LAUNCHCONFIG = "vscode.java.validateLaunchConfig";
3636

37+
public static String RESOLVE_MAINMETHOD = "vscode.java.resolveMainMethod";
38+
3739
@Override
3840
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor progress) throws Exception {
3941
if (DEBUG_STARTSESSION.equals(commandId)) {
@@ -54,6 +56,8 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
5456
return DebugSettingUtils.configDebugSettings(arguments);
5557
} else if (VALIDATE_LAUNCHCONFIG.equals(commandId)) {
5658
return new ResolveMainClassHandler().validateLaunchConfig(arguments);
59+
} else if (RESOLVE_MAINMETHOD.equals(commandId)) {
60+
return ResolveMainMethodHandler.resolveMainMethods(arguments);
5761
}
5862

5963
throw new UnsupportedOperationException(String.format("Java debug plugin doesn't support the command '%s'.", commandId));
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2018 Microsoft Corporation and others.
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+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.plugin.internal;
13+
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
16+
import java.util.Collections;
17+
import java.util.List;
18+
19+
import org.eclipse.core.resources.IProject;
20+
import org.eclipse.core.resources.IResource;
21+
import org.eclipse.jdt.core.Flags;
22+
import org.eclipse.jdt.core.ICompilationUnit;
23+
import org.eclipse.jdt.core.IJavaElement;
24+
import org.eclipse.jdt.core.IJavaProject;
25+
import org.eclipse.jdt.core.IMethod;
26+
import org.eclipse.jdt.core.ISourceRange;
27+
import org.eclipse.jdt.core.ISourceReference;
28+
import org.eclipse.jdt.core.IType;
29+
import org.eclipse.jdt.core.JavaModelException;
30+
import org.eclipse.jdt.ls.core.internal.JDTUtils;
31+
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
32+
import org.eclipse.lsp4j.Range;
33+
34+
import com.microsoft.java.debug.core.DebugException;
35+
36+
public class ResolveMainMethodHandler {
37+
/**
38+
* Resolve the main methods from the current file.
39+
* @return an array of main methods.
40+
*/
41+
public static Object resolveMainMethods(List<Object> arguments) throws DebugException {
42+
if (arguments == null || arguments.isEmpty()) {
43+
return Collections.emptyList();
44+
}
45+
46+
String uri = (String) arguments.get(0);
47+
final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uri);
48+
if (unit == null || unit.getResource() == null || !unit.getResource().exists()) {
49+
return Collections.emptyList();
50+
}
51+
52+
try {
53+
return resolveMainMethodCore(unit);
54+
} catch (Exception e) {
55+
throw new DebugException("Failed to resolve main method codeLens: " + e.getMessage(), e);
56+
}
57+
}
58+
59+
private static List<MainMethod> resolveMainMethodCore(ICompilationUnit compilationUnit) throws JavaModelException {
60+
List<MainMethod> result = new ArrayList<>();
61+
for (IMethod method : searchMainMethods(compilationUnit)) {
62+
MainMethod mainMethod = extractMainMethodInfo(compilationUnit, method);
63+
if (mainMethod != null) {
64+
result.add(mainMethod);
65+
}
66+
}
67+
68+
return result;
69+
}
70+
71+
private static List<IMethod> searchMainMethods(ICompilationUnit compilationUnit) throws JavaModelException {
72+
List<IMethod> result = new ArrayList<>();
73+
for (IType type : getPotentialMainClassTypes(compilationUnit)) {
74+
IMethod method = getMainMethod(type);
75+
if (method != null) {
76+
result.add(method);
77+
}
78+
}
79+
80+
return result;
81+
}
82+
83+
private static IMethod getMainMethod(IType type) throws JavaModelException {
84+
for (IMethod method : type.getMethods()) {
85+
// Have at most one main method in the member methods of the type.
86+
if (method.isMainMethod()) {
87+
return method;
88+
}
89+
}
90+
91+
return null;
92+
}
93+
94+
private static List<IType> getPotentialMainClassTypes(ICompilationUnit compilationUnit) throws JavaModelException {
95+
List<IType> result = new ArrayList<>();
96+
IType[] topLevelTypes = compilationUnit.getTypes();
97+
result.addAll(Arrays.asList(topLevelTypes));
98+
for (IType type : topLevelTypes) {
99+
result.addAll(getPotentialMainClassTypesInChildren(type));
100+
}
101+
102+
return result;
103+
}
104+
105+
private static List<IType> getPotentialMainClassTypesInChildren(IType type) throws JavaModelException {
106+
IType[] children = type.getTypes();
107+
if (children.length == 0) {
108+
return Collections.emptyList();
109+
}
110+
111+
List<IType> result = new ArrayList<>();
112+
for (IType child : children) {
113+
// main method can only exist in the static class or top level class.
114+
if (child.isClass() && Flags.isStatic(child.getFlags())) {
115+
result.add(child);
116+
result.addAll(getPotentialMainClassTypesInChildren(child));
117+
}
118+
}
119+
120+
return result;
121+
}
122+
123+
private static MainMethod extractMainMethodInfo(ICompilationUnit typeRoot, IMethod method) throws JavaModelException {
124+
final Range range = getRange(typeRoot, method);
125+
IResource resource = typeRoot.getResource();
126+
if (resource != null) {
127+
IProject project = resource.getProject();
128+
if (project != null) {
129+
String mainClass = method.getDeclaringType().getFullyQualifiedName();
130+
IJavaProject javaProject = JdtUtils.getJavaProject(project);
131+
if (javaProject != null) {
132+
String moduleName = JdtUtils.getModuleName(javaProject);
133+
if (moduleName != null) {
134+
mainClass = moduleName + "/" + mainClass;
135+
}
136+
}
137+
138+
String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName();
139+
return new MainMethod(range, mainClass, projectName);
140+
}
141+
}
142+
143+
return null;
144+
}
145+
146+
private static Range getRange(ICompilationUnit typeRoot, IJavaElement element) throws JavaModelException {
147+
ISourceRange r = ((ISourceReference) element).getNameRange();
148+
return JDTUtils.toRange(typeRoot, r.getOffset(), r.getLength());
149+
}
150+
151+
static class MainMethod {
152+
private Range range;
153+
private String mainClass;
154+
private String projectName;
155+
156+
public MainMethod(Range range, String mainClass, String projectName) {
157+
this.range = range;
158+
this.mainClass = mainClass;
159+
this.projectName = projectName;
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)