Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IModuleDescription;
Expand All @@ -58,6 +57,7 @@
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
Expand Down Expand Up @@ -155,7 +155,7 @@ public String getMessage() {
* Error context information for operations
*/
public static class ErrorContext {
public final String errorValue; // The value that caused the error (e.g., invalid URI, null parsedPath, etc.)
public final String errorValue; // The value that caused the error (e.g., invalid URI, null parsedPath, etc.)

public ErrorContext(String errorValue) {
this.errorValue = errorValue;
Expand Down Expand Up @@ -491,7 +491,7 @@ public static ImportClassContentResult getImportClassContent(List<Object> argume
// Record start time for timeout control
long startTime = System.currentTimeMillis();
final long TIMEOUT_MS = 80; // 80ms timeout

if (arguments == null || arguments.isEmpty()) {
return new ImportClassContentResult(ImportClassContentErrorReason.NULL_ARGUMENTS);
}
Expand All @@ -501,43 +501,24 @@ public static ImportClassContentResult getImportClassContent(List<Object> argume
if (fileUri == null || fileUri.trim().isEmpty()) {
return new ImportClassContentResult(ImportClassContentErrorReason.INVALID_URI, fileUri);
}
// Parse URI manually to avoid restricted API
java.net.URI uri = new java.net.URI(fileUri);
String filePath = uri.getPath();
if (filePath == null) {
return new ImportClassContentResult(ImportClassContentErrorReason.URI_PARSE_FAILED, filePath);
}

IPath path = new Path(filePath);
// Directly resolve compilation unit from URI using JDTUtils
java.net.URI uri = JDTUtils.toURI(fileUri);
org.eclipse.jdt.core.ICompilationUnit compilationUnit = JDTUtils.resolveCompilationUnit(uri);

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

// Get the Java project
IJavaProject javaProject = JavaCore.create(file.getProject());
if (javaProject == null) {
return new ImportClassContentResult(ImportClassContentErrorReason.NOT_JAVA_PROJECT, filePath);
}
if (!javaProject.exists()) {
String projectName = javaProject.getProject().getName();
// Get the Java project from the compilation unit
IJavaProject javaProject = compilationUnit.getJavaProject();
if (javaProject == null || !javaProject.exists()) {
String projectName = javaProject != null && javaProject.getProject() != null
? javaProject.getProject().getName()
: "unknown";
return new ImportClassContentResult(ImportClassContentErrorReason.PROJECT_NOT_EXISTS, projectName);
}

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

org.eclipse.jdt.core.ICompilationUnit compilationUnit = (org.eclipse.jdt.core.ICompilationUnit) javaElement;

// Parse imports and resolve local project files
List<ImportClassInfo> classInfoList = new ArrayList<>();

Expand Down Expand Up @@ -580,28 +561,30 @@ public static ImportClassContentResult getImportClassContent(List<Object> argume
// Check if we have exceeded the timeout before starting external resolution
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - startTime;

if (elapsedTime >= TIMEOUT_MS) {
// Return early due to timeout, but still return what we have collected so far
if (classInfoList.isEmpty()) {
return new ImportClassContentResult(ImportClassContentErrorReason.TIME_LIMIT_EXCEEDED, String.valueOf(elapsedTime) + "ms");
return new ImportClassContentResult(ImportClassContentErrorReason.TIME_LIMIT_EXCEEDED,
String.valueOf(elapsedTime) + "ms");
}
return new ImportClassContentResult(classInfoList);
}

List<ImportClassInfo> externalClasses = new ArrayList<>();

for (org.eclipse.jdt.core.IImportDeclaration importDecl : imports) {
// Check cancellation before each external resolution
if (monitor.isCanceled()) {
break;
}

// Check timeout before each external resolution
currentTime = System.currentTimeMillis();
elapsedTime = currentTime - startTime;
if (elapsedTime >= TIMEOUT_MS) {
// Timeout reached, stop processing external dependencies but keep existing results
// Timeout reached, stop processing external dependencies but keep existing
// results
break;
}

Expand Down Expand Up @@ -673,8 +656,9 @@ private static String getSeverityString(int severity) {
* Get project dependencies information including JDK version.
*
* @param arguments List containing the file URI as the first element
* @param monitor Progress monitor for cancellation support
* @return List of DependencyInfo containing key-value pairs of project information
* @param monitor Progress monitor for cancellation support
* @return List of DependencyInfo containing key-value pairs of project
* information
*/
public static ProjectDependenciesResult getProjectDependencies(List<Object> arguments,
IProgressMonitor monitor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.JDTUtils;

import com.microsoft.jdtls.ext.core.JdtlsExtActivator;

Expand Down Expand Up @@ -184,15 +185,19 @@ public static void resolveSingleType(IJavaProject javaProject, String typeName,
if (packageFragment != null && packageFragment.exists()) {
// Look for compilation unit with matching name
org.eclipse.jdt.core.ICompilationUnit cu = packageFragment.getCompilationUnit(simpleName + ".java");
if (cu != null && cu.exists() && cu.getResource() != null && cu.getResource().exists()) {
// Get primary type from compilation unit
org.eclipse.jdt.core.IType primaryType = cu.findPrimaryType();
if (primaryType != null && primaryType.exists() &&
typeName.equals(primaryType.getFullyQualifiedName())) {
// Found local project source type via fallback method
processedTypes.add(typeName);
extractTypeInfo(primaryType, classInfoList, monitor);
return;
if (cu != null && cu.exists()) {
// Use JDTUtils to check if the compilation unit is accessible
String cuUri = JDTUtils.toUri(cu);
if (cuUri != null) {
// Get primary type from compilation unit
org.eclipse.jdt.core.IType primaryType = cu.findPrimaryType();
if (primaryType != null && primaryType.exists() &&
typeName.equals(primaryType.getFullyQualifiedName())) {
// Found local project source type via fallback method
processedTypes.add(typeName);
extractTypeInfo(primaryType, classInfoList, monitor);
return;
}
}

// Also check for inner types in the compilation unit
Expand Down Expand Up @@ -685,18 +690,19 @@ public static String getTypeUri(org.eclipse.jdt.core.IType type) {
// Get the compilation unit that contains this type
org.eclipse.jdt.core.ICompilationUnit compilationUnit = type.getCompilationUnit();
if (compilationUnit != null) {
// Get the underlying resource (file)
org.eclipse.core.resources.IResource resource = compilationUnit.getUnderlyingResource();
if (resource != null && resource instanceof org.eclipse.core.resources.IFile) {
org.eclipse.core.resources.IFile file = (org.eclipse.core.resources.IFile) resource;
// Get the file location as a file URI
java.net.URI fileUri = file.getLocationURI();
if (fileUri != null) {
return fileUri.toString();
}

// Fallback: use workspace-relative path as URI
return file.getFullPath().toString();
// Use JDTUtils to get URI (consistent with other parts of the codebase)
String uri = JDTUtils.toUri(compilationUnit);
if (uri != null) {
return uri;
}
}

// For class files (binary types), try to get URI from class file
org.eclipse.jdt.core.IClassFile classFile = type.getClassFile();
if (classFile != null) {
String uri = JDTUtils.toUri(classFile);
if (uri != null) {
return uri;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
Expand All @@ -25,7 +24,7 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.JDTUtils;

import com.microsoft.jdtls.ext.core.JdtlsExtActivator;

Expand Down Expand Up @@ -171,10 +170,9 @@ private static void invalidateCache(IProject project) {
return;
}

String projectPath = project.getLocation() != null ?
project.getLocation().toOSString() : project.getName();
String projectUri = JDTUtils.getFileURI(project);

if (dependencyCache.remove(projectPath) != null) {
if (dependencyCache.remove(projectUri) != null) {
JdtlsExtActivator.logInfo("Cache invalidated for project: " + project.getName());
}
}
Expand Down Expand Up @@ -214,7 +212,6 @@ private static long calculateClasspathHash(IJavaProject javaProject) {
private static final String KEY_MODULE_NAME = "moduleName";
private static final String KEY_TOTAL_LIBRARIES = "totalLibraries";
private static final String KEY_TOTAL_PROJECT_REFS = "totalProjectReferences";
private static final String KEY_JRE_CONTAINER_PATH = "jreContainerPath";
private static final String KEY_JRE_CONTAINER = "jreContainer";

public static class DependencyInfo {
Expand Down Expand Up @@ -242,12 +239,16 @@ public static List<DependencyInfo> resolveProjectDependencies(String fileUri, IP
List<DependencyInfo> result = new ArrayList<>();

try {
IPath fileIPath = ResourceUtils.canonicalFilePathFromURI(fileUri);
// Use JDTUtils to convert URI and find the resource
java.net.URI uri = JDTUtils.toURI(fileUri);
IResource resource = JDTUtils.findResource(uri,
ResourcesPlugin.getWorkspace().getRoot()::findFilesForLocationURI);

// Find the project
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject project = findProjectByPath(root, fileIPath);
if (resource == null) {
return result;
}

IProject project = resource.getProject();
if (project == null || !project.isAccessible()) {
return result;
}
Expand All @@ -258,8 +259,8 @@ public static List<DependencyInfo> resolveProjectDependencies(String fileUri, IP
return result;
}

// Generate cache key based on project location
String cacheKey = project.getLocation().toOSString();
// Generate cache key based on project URI
String cacheKey = JDTUtils.getFileURI(project);

// Calculate current classpath hash for validation
long currentClasspathHash = calculateClasspathHash(javaProject);
Expand Down Expand Up @@ -290,44 +291,13 @@ public static List<DependencyInfo> resolveProjectDependencies(String fileUri, IP
return result;
}

/**
* Find project by path from all projects in workspace.
* The path can be either a project root path or a file/folder path within a project.
* This method will find the project that contains the given path.
*
* @param root The workspace root
* @param filePath The path to search for (can be project root or file within project)
* @return The project that contains the path, or null if not found
*/
private static IProject findProjectByPath(IWorkspaceRoot root, IPath filePath) {
IProject[] allProjects = root.getProjects();

// First pass: check for exact project location match (most efficient)
for (IProject p : allProjects) {
if (p.getLocation() != null && p.getLocation().equals(filePath)) {
return p;
}
}

// Second pass: check if the file path is within any project directory
// This handles cases where filePath points to a file or folder inside a project
for (IProject p : allProjects) {
if (p.getLocation() != null && p.getLocation().isPrefixOf(filePath)) {
return p;
}
}

return null;
}

/**
* Add basic project information including name, location, and Java version settings.
*/
private static void addBasicProjectInfo(List<DependencyInfo> result, IProject project, IJavaProject javaProject) {
result.add(new DependencyInfo(KEY_PROJECT_NAME, project.getName()));

addIfNotNull(result, KEY_PROJECT_LOCATION,
project.getLocation() != null ? project.getLocation().toOSString() : null);
addIfNotNull(result, KEY_PROJECT_LOCATION, JDTUtils.getFileURI(project));

addIfNotNull(result, KEY_JAVA_VERSION,
javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true));
Expand Down
Loading