Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<command id="java.project.checkImportStatus" />
<command id="java.project.getImportClassContent" />
<command id="java.project.getDependencies" />
<command id="java.project.getImportClassContentWithResult" />
<command id="java.project.getProjectDependenciesWithResult" />
</delegateCommandHandler>
</extension>
<extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
return ProjectCommand.getImportClassContent(arguments, monitor);
case "java.project.getDependencies":
return ProjectCommand.getProjectDependencies(arguments, monitor);
case "java.project.getImportClassContentWithResult":
return ProjectCommand.getImportClassContentWithResult(arguments, monitor);
case "java.project.getProjectDependenciesWithResult":
return ProjectCommand.getProjectDependenciesWithResult(arguments, monitor);
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,101 @@ public DependencyInfo(String key, String value) {
}
}

/**
* Empty reasons for ImportClassContent operation
*/
public enum ImportClassContentErrorReason {
NULL_ARGUMENTS("NullArgs"),
INVALID_URI("InvalidUri"),
URI_PARSE_FAILED("UriParseFail"),
FILE_NOT_FOUND("FileNotFound"),
FILE_NOT_EXISTS("FileNotExists"),
NOT_JAVA_PROJECT("NotJavaProject"),
PROJECT_NOT_EXISTS("ProjectNotExists"),
NOT_COMPILATION_UNIT("NotCompilationUnit"),
NO_IMPORTS("NoImports"),
OPERATION_CANCELLED("Cancelled"),
TIME_LIMIT_EXCEEDED("Timeout"),
NO_RESULTS("NoResults"),
PROCESSING_EXCEPTION("ProcessingError");

private final String message;

ImportClassContentErrorReason(String message) {
this.message = message;
}

public String getMessage() {
return message;
}
}

/**
* Empty reasons for ProjectDependencies operation
*/
public enum ProjectDependenciesErrorReason {
NULL_ARGUMENTS("NullArgs"),
INVALID_URI("InvalidUri"),
URI_PARSE_FAILED("UriParseFail"),
MALFORMED_URI("MalformedUri"),
OPERATION_CANCELLED("Cancelled"),
RESOLVER_NULL_RESULT("ResolverNull"),
NO_DEPENDENCIES("NoDependencies"),
PROCESSING_EXCEPTION("ProcessingError");

private final String message;

ProjectDependenciesErrorReason(String message) {
this.message = message;
}

public String getMessage() {
return message;
}
}

/**
* Result wrapper for getImportClassContent method
*/
public static class ImportClassContentResult {
public List<ImportClassInfo> classInfoList;
public String emptyReason; // Reason why the result is empty
public boolean isEmpty;

public ImportClassContentResult(List<ImportClassInfo> classInfoList) {
this.classInfoList = classInfoList;
this.emptyReason = null;
this.isEmpty = false;
}

public ImportClassContentResult(ImportClassContentErrorReason errorReason) {
this.classInfoList = Collections.emptyList();
this.emptyReason = errorReason.getMessage(); // Use enum message
this.isEmpty = true;
}
}

/**
* Result wrapper for getProjectDependencies method
*/
public static class ProjectDependenciesResult {
public List<DependencyInfo> dependencyInfoList;
public String emptyReason; // Reason why the result is empty
public boolean isEmpty;

public ProjectDependenciesResult(List<DependencyInfo> dependencyInfoList) {
this.dependencyInfoList = dependencyInfoList;
this.emptyReason = null;
this.isEmpty = false;
}

public ProjectDependenciesResult(ProjectDependenciesErrorReason errorReason) {
this.dependencyInfoList = new ArrayList<>();
this.emptyReason = errorReason.getMessage(); // Use enum message
this.isEmpty = true;
}
}

private static class Classpath {
public String source;
public String destination;
Expand Down Expand Up @@ -350,18 +445,24 @@ public static boolean checkImportStatus() {
return hasError;
}

// This method reserver for pack.
public static List<ImportClassInfo> getImportClassContent(List<Object> arguments, IProgressMonitor monitor) {
ImportClassContentResult result = getImportClassContentWithResult(arguments, monitor);
return result == null ? Collections.emptyList() : result.classInfoList;
}

/**
* Get import class content for Copilot integration.
* Get import class content for Copilot integration with detailed error reporting.
* This method extracts information about imported classes from a Java file.
* Uses a time-controlled strategy: prioritizes internal classes, adds external classes only if time permits.
*
* @param arguments List containing the file URI as the first element
* @param monitor Progress monitor for cancellation support
* @return List of ImportClassInfo containing class information and JavaDoc
* @return ImportClassContentResult containing class information and error reason if applicable
*/
public static List<ImportClassInfo> getImportClassContent(List<Object> arguments, IProgressMonitor monitor) {
public static ImportClassContentResult getImportClassContentWithResult(List<Object> arguments, IProgressMonitor monitor) {
if (arguments == null || arguments.isEmpty()) {
return Collections.emptyList();
return new ImportClassContentResult(ImportClassContentErrorReason.NULL_ARGUMENTS);
}

// Time control: total budget 80ms, early return at 75ms
Expand All @@ -371,33 +472,42 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments

try {
String fileUri = (String) arguments.get(0);
if (fileUri == null || fileUri.trim().isEmpty()) {
return new ImportClassContentResult(ImportClassContentErrorReason.INVALID_URI);
}

// Parse URI manually to avoid restricted API
java.net.URI uri = new java.net.URI(fileUri);
String filePath = uri.getPath();
if (filePath == null) {
return Collections.emptyList();
return new ImportClassContentResult(ImportClassContentErrorReason.URI_PARSE_FAILED);
}

IPath path = new Path(filePath);

// Get the file resource
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IFile file = root.getFileForLocation(path);
if (file == null || !file.exists()) {
return Collections.emptyList();
if (file == null) {
return new ImportClassContentResult(ImportClassContentErrorReason.FILE_NOT_FOUND);
}
if (!file.exists()) {
return new ImportClassContentResult(ImportClassContentErrorReason.FILE_NOT_EXISTS);
}

// Get the Java project
IJavaProject javaProject = JavaCore.create(file.getProject());
if (javaProject == null || !javaProject.exists()) {
return Collections.emptyList();
if (javaProject == null) {
return new ImportClassContentResult(ImportClassContentErrorReason.NOT_JAVA_PROJECT);
}
if (!javaProject.exists()) {
return new ImportClassContentResult(ImportClassContentErrorReason.PROJECT_NOT_EXISTS);
}

// Find the compilation unit
IJavaElement javaElement = JavaCore.create(file);
if (!(javaElement instanceof org.eclipse.jdt.core.ICompilationUnit)) {
return Collections.emptyList();
return new ImportClassContentResult(ImportClassContentErrorReason.NOT_COMPILATION_UNIT);
}

org.eclipse.jdt.core.ICompilationUnit compilationUnit = (org.eclipse.jdt.core.ICompilationUnit) javaElement;
Expand All @@ -409,12 +519,20 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
org.eclipse.jdt.core.IImportDeclaration[] imports = compilationUnit.getImports();
Set<String> processedTypes = new HashSet<>();

// Check if file has no imports
if (imports == null || imports.length == 0) {
return new ImportClassContentResult(ImportClassContentErrorReason.NO_IMPORTS);
}

// Phase 1: Priority - Resolve project source classes (internal)
for (org.eclipse.jdt.core.IImportDeclaration importDecl : imports) {
// Check time budget before each operation
long elapsed = System.currentTimeMillis() - startTime;
if (monitor.isCanceled() || elapsed >= EARLY_RETURN_MS) {
return classInfoList; // Early return if approaching time limit
if (monitor.isCanceled()) {
return new ImportClassContentResult(ImportClassContentErrorReason.OPERATION_CANCELLED);
}
if (elapsed >= EARLY_RETURN_MS) {
return new ImportClassContentResult(ImportClassContentErrorReason.TIME_LIMIT_EXCEEDED);
}

String importName = importDecl.getElementName();
Expand Down Expand Up @@ -470,11 +588,15 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
}
}

return classInfoList;
// Success case - return the resolved class information
if (classInfoList.isEmpty()) {
return new ImportClassContentResult(ImportClassContentErrorReason.NO_RESULTS);
}
return new ImportClassContentResult(classInfoList);

} catch (Exception e) {
JdtlsExtActivator.logException("Error in getImportClassContent", e);
return Collections.emptyList();
return new ImportClassContentResult(ImportClassContentErrorReason.PROCESSING_EXCEPTION);
}
}

Expand Down Expand Up @@ -503,28 +625,73 @@ private static String getSeverityString(int severity) {
}
}

// resverved for pack.
public static List<DependencyInfo> getProjectDependencies(List<Object> arguments, IProgressMonitor monitor) {
ProjectDependenciesResult result = getProjectDependenciesWithResult(arguments, monitor);
return result == null ? Collections.emptyList() : result.dependencyInfoList;
}


/**
* Get project dependencies information including JDK version.
* Get project dependencies information with detailed error reporting.
* This method extracts project dependency information including JDK version, build tool, etc.
*
* @param arguments List containing the project URI as the first element
* @param monitor Progress monitor for cancellation support
* @return List of DependencyInfo containing key-value pairs of project information
* @return ProjectDependenciesResult containing dependency information and error reason if applicable
*/
public static List<DependencyInfo> getProjectDependencies(List<Object> arguments, IProgressMonitor monitor) {
public static ProjectDependenciesResult getProjectDependenciesWithResult(List<Object> arguments, IProgressMonitor monitor) {
if (arguments == null || arguments.isEmpty()) {
return new ArrayList<>();
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.NULL_ARGUMENTS);
}

String projectUri = (String) arguments.get(0);
List<ProjectResolver.DependencyInfo> resolverResult = ProjectResolver.resolveProjectDependencies(projectUri, monitor);

// Convert ProjectResolver.DependencyInfo to ProjectCommand.DependencyInfo
List<DependencyInfo> result = new ArrayList<>();
for (ProjectResolver.DependencyInfo info : resolverResult) {
result.add(new DependencyInfo(info.key, info.value));
try {
String projectUri = (String) arguments.get(0);
if (projectUri == null || projectUri.trim().isEmpty()) {
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.INVALID_URI);
}

// Validate URI format
try {
java.net.URI uri = new java.net.URI(projectUri);
if (uri.getPath() == null) {
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.URI_PARSE_FAILED);
}
} catch (java.net.URISyntaxException e) {
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.MALFORMED_URI);
}

// Check if monitor is cancelled before processing
if (monitor.isCanceled()) {
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.OPERATION_CANCELLED);
}

List<ProjectResolver.DependencyInfo> resolverResult = ProjectResolver.resolveProjectDependencies(projectUri, monitor);

// Check if resolver returned null (should not happen, but defensive programming)
if (resolverResult == null) {
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.RESOLVER_NULL_RESULT);
}

// Convert ProjectResolver.DependencyInfo to ProjectCommand.DependencyInfo
List<DependencyInfo> result = new ArrayList<>();
for (ProjectResolver.DependencyInfo info : resolverResult) {
if (info != null) {
result.add(new DependencyInfo(info.key, info.value));
}
}

// Check if no dependencies were resolved
if (result.isEmpty()) {
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.NO_DEPENDENCIES);
}

return new ProjectDependenciesResult(result);

} catch (Exception e) {
JdtlsExtActivator.logException("Error in getProjectDependenciesWithReason", e);
return new ProjectDependenciesResult(ProjectDependenciesErrorReason.PROCESSING_EXCEPTION);
}

return result;
}

private static final class LinkedFolderVisitor implements IResourceVisitor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ public class ContextResolver {
*/
public static class ImportClassInfo {
public String uri; // File URI (required)
public String className; // Human-readable class description with JavaDoc appended (required)
public String value; // Human-readable class description with JavaDoc appended (required)

public ImportClassInfo(String uri, String value) {
this.uri = uri;
this.className = value;
this.value = value;
}
}

Expand Down
Loading
Loading