@@ -98,6 +98,101 @@ public DependencyInfo(String key, String value) {
9898 }
9999 }
100100
101+ /**
102+ * Empty reasons for ImportClassContent operation
103+ */
104+ public enum ImportClassContentErrorReason {
105+ NULL_ARGUMENTS ("NullArgs" ),
106+ INVALID_URI ("InvalidUri" ),
107+ URI_PARSE_FAILED ("UriParseFail" ),
108+ FILE_NOT_FOUND ("FileNotFound" ),
109+ FILE_NOT_EXISTS ("FileNotExists" ),
110+ NOT_JAVA_PROJECT ("NotJavaProject" ),
111+ PROJECT_NOT_EXISTS ("ProjectNotExists" ),
112+ NOT_COMPILATION_UNIT ("NotCompilationUnit" ),
113+ NO_IMPORTS ("NoImports" ),
114+ OPERATION_CANCELLED ("Cancelled" ),
115+ TIME_LIMIT_EXCEEDED ("Timeout" ),
116+ NO_RESULTS ("NoResults" ),
117+ PROCESSING_EXCEPTION ("ProcessingError" );
118+
119+ private final String message ;
120+
121+ ImportClassContentErrorReason (String message ) {
122+ this .message = message ;
123+ }
124+
125+ public String getMessage () {
126+ return message ;
127+ }
128+ }
129+
130+ /**
131+ * Empty reasons for ProjectDependencies operation
132+ */
133+ public enum ProjectDependenciesErrorReason {
134+ NULL_ARGUMENTS ("NullArgs" ),
135+ INVALID_URI ("InvalidUri" ),
136+ URI_PARSE_FAILED ("UriParseFail" ),
137+ MALFORMED_URI ("MalformedUri" ),
138+ OPERATION_CANCELLED ("Cancelled" ),
139+ RESOLVER_NULL_RESULT ("ResolverNull" ),
140+ NO_DEPENDENCIES ("NoDependencies" ),
141+ PROCESSING_EXCEPTION ("ProcessingError" );
142+
143+ private final String message ;
144+
145+ ProjectDependenciesErrorReason (String message ) {
146+ this .message = message ;
147+ }
148+
149+ public String getMessage () {
150+ return message ;
151+ }
152+ }
153+
154+ /**
155+ * Result wrapper for getImportClassContent method
156+ */
157+ public static class ImportClassContentResult {
158+ public List <ImportClassInfo > classInfoList ;
159+ public String emptyReason ; // Reason why the result is empty
160+ public boolean isEmpty ;
161+
162+ public ImportClassContentResult (List <ImportClassInfo > classInfoList ) {
163+ this .classInfoList = classInfoList ;
164+ this .emptyReason = null ;
165+ this .isEmpty = false ;
166+ }
167+
168+ public ImportClassContentResult (ImportClassContentErrorReason errorReason ) {
169+ this .classInfoList = Collections .emptyList ();
170+ this .emptyReason = errorReason .getMessage (); // Use enum message
171+ this .isEmpty = true ;
172+ }
173+ }
174+
175+ /**
176+ * Result wrapper for getProjectDependencies method
177+ */
178+ public static class ProjectDependenciesResult {
179+ public List <DependencyInfo > dependencyInfoList ;
180+ public String emptyReason ; // Reason why the result is empty
181+ public boolean isEmpty ;
182+
183+ public ProjectDependenciesResult (List <DependencyInfo > dependencyInfoList ) {
184+ this .dependencyInfoList = dependencyInfoList ;
185+ this .emptyReason = null ;
186+ this .isEmpty = false ;
187+ }
188+
189+ public ProjectDependenciesResult (ProjectDependenciesErrorReason errorReason ) {
190+ this .dependencyInfoList = new ArrayList <>();
191+ this .emptyReason = errorReason .getMessage (); // Use enum message
192+ this .isEmpty = true ;
193+ }
194+ }
195+
101196 private static class Classpath {
102197 public String source ;
103198 public String destination ;
@@ -350,18 +445,38 @@ public static boolean checkImportStatus() {
350445 return hasError ;
351446 }
352447
448+ /**
449+ * Get import class content for Copilot integration (backward compatibility
450+ * wrapper).
451+ * This method maintains compatibility with the original return type.
452+ *
453+ * @param arguments List containing the file URI as the first element
454+ * @param monitor Progress monitor for cancellation support
455+ * @return List of ImportClassInfo containing class information and JavaDoc
456+ */
457+ public static List <ImportClassInfo > getImportClassContent (List <Object > arguments , IProgressMonitor monitor ) {
458+ ImportClassContentResult result = getImportClassContentWithResult (arguments , monitor );
459+ if (result .isEmpty ) {
460+ // Log the error reason for debugging
461+ JdtlsExtActivator .logError ("getImportClassContent failed: " + result .emptyReason );
462+ }
463+ return result .classInfoList ;
464+ }
465+
353466 /**
354467 * Get import class content for Copilot integration.
355468 * This method extracts information about imported classes from a Java file.
356- * Uses a time-controlled strategy: prioritizes internal classes, adds external classes only if time permits.
469+ * Uses a time-controlled strategy: prioritizes internal classes, adds external
470+ * classes only if time permits.
357471 *
358472 * @param arguments List containing the file URI as the first element
359- * @param monitor Progress monitor for cancellation support
473+ * @param monitor Progress monitor for cancellation support
360474 * @return List of ImportClassInfo containing class information and JavaDoc
361475 */
362- public static List <ImportClassInfo > getImportClassContent (List <Object > arguments , IProgressMonitor monitor ) {
476+ public static ImportClassContentResult getImportClassContentWithResult (List <Object > arguments ,
477+ IProgressMonitor monitor ) {
363478 if (arguments == null || arguments .isEmpty ()) {
364- return Collections . emptyList ( );
479+ return new ImportClassContentResult ( ImportClassContentErrorReason . NULL_ARGUMENTS );
365480 }
366481
367482 // Time control: total budget 80ms, early return at 75ms
@@ -371,12 +486,14 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
371486
372487 try {
373488 String fileUri = (String ) arguments .get (0 );
374-
489+ if (fileUri == null || fileUri .trim ().isEmpty ()) {
490+ return new ImportClassContentResult (ImportClassContentErrorReason .INVALID_URI );
491+ }
375492 // Parse URI manually to avoid restricted API
376493 java .net .URI uri = new java .net .URI (fileUri );
377494 String filePath = uri .getPath ();
378495 if (filePath == null ) {
379- return Collections . emptyList ( );
496+ return new ImportClassContentResult ( ImportClassContentErrorReason . URI_PARSE_FAILED );
380497 }
381498
382499 IPath path = new Path (filePath );
@@ -385,19 +502,25 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
385502 IWorkspaceRoot root = ResourcesPlugin .getWorkspace ().getRoot ();
386503 IFile file = root .getFileForLocation (path );
387504 if (file == null || !file .exists ()) {
388- return Collections .emptyList ();
505+ return new ImportClassContentResult (ImportClassContentErrorReason .FILE_NOT_FOUND );
506+ }
507+ if (!file .exists ()) {
508+ return new ImportClassContentResult (ImportClassContentErrorReason .FILE_NOT_EXISTS );
389509 }
390510
391511 // Get the Java project
392512 IJavaProject javaProject = JavaCore .create (file .getProject ());
393- if (javaProject == null || !javaProject .exists ()) {
394- return Collections .emptyList ();
513+ if (javaProject == null ) {
514+ return new ImportClassContentResult (ImportClassContentErrorReason .NOT_JAVA_PROJECT );
515+ }
516+ if (!javaProject .exists ()) {
517+ return new ImportClassContentResult (ImportClassContentErrorReason .PROJECT_NOT_EXISTS );
395518 }
396519
397520 // Find the compilation unit
398521 IJavaElement javaElement = JavaCore .create (file );
399522 if (!(javaElement instanceof org .eclipse .jdt .core .ICompilationUnit )) {
400- return Collections . emptyList ( );
523+ return new ImportClassContentResult ( ImportClassContentErrorReason . NOT_COMPILATION_UNIT );
401524 }
402525
403526 org .eclipse .jdt .core .ICompilationUnit compilationUnit = (org .eclipse .jdt .core .ICompilationUnit ) javaElement ;
@@ -409,24 +532,34 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
409532 org .eclipse .jdt .core .IImportDeclaration [] imports = compilationUnit .getImports ();
410533 Set <String > processedTypes = new HashSet <>();
411534
535+ // Check if file has no imports
536+ if (imports == null || imports .length == 0 ) {
537+ return new ImportClassContentResult (ImportClassContentErrorReason .NO_IMPORTS );
538+ }
539+
412540 // Phase 1: Priority - Resolve project source classes (internal)
413541 for (org .eclipse .jdt .core .IImportDeclaration importDecl : imports ) {
414542 // Check time budget before each operation
415543 long elapsed = System .currentTimeMillis () - startTime ;
416- if (monitor .isCanceled () || elapsed >= EARLY_RETURN_MS ) {
417- return classInfoList ; // Early return if approaching time limit
544+ if (monitor .isCanceled ()) {
545+ return new ImportClassContentResult (ImportClassContentErrorReason .OPERATION_CANCELLED );
546+ }
547+ if (elapsed >= EARLY_RETURN_MS ) {
548+ return new ImportClassContentResult (ImportClassContentErrorReason .TIME_LIMIT_EXCEEDED );
418549 }
419550
420551 String importName = importDecl .getElementName ();
421552 boolean isStatic = (importDecl .getFlags () & org .eclipse .jdt .core .Flags .AccStatic ) != 0 ;
422-
553+
423554 if (isStatic ) {
424555 // Handle static imports - delegate to ContextResolver
425- ContextResolver .resolveStaticImport (javaProject , importName , classInfoList , processedTypes , monitor );
556+ ContextResolver .resolveStaticImport (javaProject , importName , classInfoList , processedTypes ,
557+ monitor );
426558 } else if (importName .endsWith (".*" )) {
427559 // Handle package imports - delegate to ContextResolver
428560 String packageName = importName .substring (0 , importName .length () - 2 );
429- ContextResolver .resolvePackageTypes (javaProject , packageName , classInfoList , processedTypes , monitor );
561+ ContextResolver .resolvePackageTypes (javaProject , packageName , classInfoList , processedTypes ,
562+ monitor );
430563 } else {
431564 // Handle single type imports - delegate to ContextResolver
432565 ContextResolver .resolveSingleType (javaProject , importName , classInfoList , processedTypes , monitor );
@@ -438,11 +571,11 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
438571 if (elapsedAfterInternal < EARLY_RETURN_MS && !monitor .isCanceled ()) {
439572 // Calculate remaining time budget for external classes
440573 long remainingTime = TIME_BUDGET_MS - elapsedAfterInternal ;
441-
574+
442575 // Only proceed with external if we have reasonable time left (at least 15ms)
443576 if (remainingTime >= 15 ) {
444577 List <ImportClassInfo > externalClasses = new ArrayList <>();
445-
578+
446579 for (org .eclipse .jdt .core .IImportDeclaration importDecl : imports ) {
447580 // Check time before each external resolution
448581 long currentElapsed = System .currentTimeMillis () - startTime ;
@@ -452,29 +585,32 @@ public static List<ImportClassInfo> getImportClassContent(List<Object> arguments
452585
453586 String importName = importDecl .getElementName ();
454587 boolean isStatic = (importDecl .getFlags () & org .eclipse .jdt .core .Flags .AccStatic ) != 0 ;
455-
588+
456589 // Skip package imports (*.* ) - too broad for external dependencies
457590 if (importName .endsWith (".*" )) {
458591 continue ;
459592 }
460-
593+
461594 // Resolve external (binary) types with simplified content
462595 if (!isStatic ) {
463- ContextResolver .resolveBinaryType (javaProject , importName , externalClasses ,
596+ ContextResolver .resolveBinaryType (javaProject , importName , externalClasses ,
464597 processedTypes , Integer .MAX_VALUE , monitor );
465598 }
466599 }
467-
600+
468601 // Append external classes after project sources
469602 classInfoList .addAll (externalClasses );
470603 }
471604 }
472-
473- return classInfoList ;
605+ // Success case - return the resolved class information
606+ if (classInfoList .isEmpty ()) {
607+ return new ImportClassContentResult (ImportClassContentErrorReason .NO_RESULTS );
608+ }
609+ return new ImportClassContentResult (classInfoList );
474610
475611 } catch (Exception e ) {
476612 JdtlsExtActivator .logException ("Error in getImportClassContent" , e );
477- return Collections . emptyList ( );
613+ return new ImportClassContentResult ( ImportClassContentErrorReason . PROCESSING_EXCEPTION );
478614 }
479615 }
480616
@@ -503,28 +639,70 @@ private static String getSeverityString(int severity) {
503639 }
504640 }
505641
642+ public static List <DependencyInfo > getProjectDependencies (List <Object > arguments , IProgressMonitor monitor ) {
643+ ProjectDependenciesResult result = getProjectDependenciesWithResult (arguments , monitor );
644+ return result == null ? Collections .emptyList () : result .dependencyInfoList ;
645+ }
646+
506647 /**
507648 * Get project dependencies information including JDK version.
508649 *
509650 * @param arguments List containing the project URI as the first element
510- * @param monitor Progress monitor for cancellation support
511- * @return List of DependencyInfo containing key-value pairs of project information
651+ * @param monitor Progress monitor for cancellation support
652+ * @return List of DependencyInfo containing key-value pairs of project
653+ * information
512654 */
513- public static List <DependencyInfo > getProjectDependencies (List <Object > arguments , IProgressMonitor monitor ) {
655+ public static ProjectDependenciesResult getProjectDependenciesWithResult (List <Object > arguments ,
656+ IProgressMonitor monitor ) {
514657 if (arguments == null || arguments .isEmpty ()) {
515- return new ArrayList <>( );
658+ return new ProjectDependenciesResult ( ProjectDependenciesErrorReason . NULL_ARGUMENTS );
516659 }
517660
518- String projectUri = (String ) arguments .get (0 );
519- List <ProjectResolver .DependencyInfo > resolverResult = ProjectResolver .resolveProjectDependencies (projectUri , monitor );
520-
521- // Convert ProjectResolver.DependencyInfo to ProjectCommand.DependencyInfo
522- List <DependencyInfo > result = new ArrayList <>();
523- for (ProjectResolver .DependencyInfo info : resolverResult ) {
524- result .add (new DependencyInfo (info .key , info .value ));
661+ try {
662+ String projectUri = (String ) arguments .get (0 );
663+ if (projectUri == null || projectUri .trim ().isEmpty ()) {
664+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .INVALID_URI );
665+ }
666+
667+ // Validate URI format
668+ try {
669+ java .net .URI uri = new java .net .URI (projectUri );
670+ if (uri .getPath () == null ) {
671+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .URI_PARSE_FAILED );
672+ }
673+ } catch (java .net .URISyntaxException e ) {
674+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .MALFORMED_URI );
675+ }
676+
677+ // Check if monitor is cancelled before processing
678+ if (monitor .isCanceled ()) {
679+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .OPERATION_CANCELLED );
680+ }
681+ List <ProjectResolver .DependencyInfo > resolverResult = ProjectResolver .resolveProjectDependencies (projectUri ,
682+ monitor );
683+ // Check if resolver returned null (should not happen, but defensive
684+ // programming)
685+ if (resolverResult == null ) {
686+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .RESOLVER_NULL_RESULT );
687+ }
688+ // Convert ProjectResolver.DependencyInfo to ProjectCommand.DependencyInfo
689+ List <DependencyInfo > result = new ArrayList <>();
690+ for (ProjectResolver .DependencyInfo info : resolverResult ) {
691+ if (info != null ) {
692+ result .add (new DependencyInfo (info .key , info .value ));
693+ }
694+ }
695+
696+ // Check if no dependencies were resolved
697+ if (result .isEmpty ()) {
698+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .NO_DEPENDENCIES );
699+ }
700+
701+ return new ProjectDependenciesResult (result );
702+ } catch (Exception e ) {
703+ JdtlsExtActivator .logException ("Error in getProjectDependenciesWithReason" , e );
704+ return new ProjectDependenciesResult (ProjectDependenciesErrorReason .PROCESSING_EXCEPTION );
525705 }
526-
527- return result ;
528706 }
529707
530708 private static final class LinkedFolderVisitor implements IResourceVisitor {
0 commit comments