@@ -49,19 +49,19 @@ public DependencyInfo(String key, String value) {
4949 * Resolve project dependencies information including JDK version.
5050 * Supports both single projects and multi-module aggregator projects.
5151 *
52- * @param projectUri The project URI
52+ * @param fileUri The file URI
5353 * @param monitor Progress monitor for cancellation support
5454 * @return List of DependencyInfo containing key-value pairs of project information
5555 */
56- public static List <DependencyInfo > resolveProjectDependencies (String projectUri , IProgressMonitor monitor ) {
56+ public static List <DependencyInfo > resolveProjectDependencies (String fileUri , IProgressMonitor monitor ) {
5757 List <DependencyInfo > result = new ArrayList <>();
5858
5959 try {
60- IPath projectPath = ResourceUtils .canonicalFilePathFromURI (projectUri );
60+ IPath fileIPath = ResourceUtils .canonicalFilePathFromURI (fileUri );
6161
6262 // Find the project
6363 IWorkspaceRoot root = ResourcesPlugin .getWorkspace ().getRoot ();
64- IProject project = findProjectByPath (root , projectPath );
64+ IProject project = findProjectByPath (root , fileIPath );
6565
6666 if (project == null || !project .isAccessible ()) {
6767 return result ;
@@ -70,11 +70,7 @@ public static List<DependencyInfo> resolveProjectDependencies(String projectUri,
7070 IJavaProject javaProject = JavaCore .create (project );
7171 // Check if this is a Java project
7272 if (javaProject == null || !javaProject .exists ()) {
73- // Not a Java project - might be an aggregator/parent project
74- // Try to find Java sub-projects under this path
75- JdtlsExtActivator .logInfo ("Not a Java project: " + project .getName () +
76- ", checking for sub-projects" );
77- return resolveAggregatorProjectDependencies (root , projectPath , monitor );
73+ return result ;
7874 }
7975
8076 // Add basic project information
@@ -92,221 +88,34 @@ public static List<DependencyInfo> resolveProjectDependencies(String projectUri,
9288
9389 return result ;
9490 }
95-
91+
9692 /**
97- * Resolve dependencies for an aggregator/parent project by finding and processing all Java sub- projects.
98- * This handles multi-module Maven/Gradle projects where the parent is not a Java project itself .
99- * Returns aggregated information useful for AI context (Java version, common dependencies, build tool) .
93+ * Find project by path from all projects in workspace .
94+ * The path can be either a project root path or a file/folder path within a project .
95+ * This method will find the project that contains the given path .
10096 *
10197 * @param root The workspace root
102- * @param parentPath The path of the parent/aggregator project
103- * @param monitor Progress monitor
104- * @return Aggregated dependency information from all sub-projects
98+ * @param filePath The path to search for (can be project root or file within project)
99+ * @return The project that contains the path, or null if not found
105100 */
106- private static List <DependencyInfo > resolveAggregatorProjectDependencies (
107- IWorkspaceRoot root , IPath parentPath , IProgressMonitor monitor ) {
108-
109- List <DependencyInfo > result = new ArrayList <>();
110- List <IJavaProject > javaProjects = new ArrayList <>();
111-
112- // Find all Java projects under the parent path
101+ private static IProject findProjectByPath (IWorkspaceRoot root , IPath filePath ) {
113102 IProject [] allProjects = root .getProjects ();
114- for (IProject p : allProjects ) {
115- if (p .getLocation () != null && parentPath .isPrefixOf (p .getLocation ())) {
116- try {
117- if (p .isAccessible () && p .hasNature (JavaCore .NATURE_ID )) {
118- IJavaProject jp = JavaCore .create (p );
119- if (jp != null && jp .exists ()) {
120- javaProjects .add (jp );
121- }
122- }
123- } catch (CoreException e ) {
124- // Skip this project
125- }
126- }
127- }
128-
129- if (javaProjects .isEmpty ()) {
130- JdtlsExtActivator .logInfo ("No Java sub-projects found under: " + parentPath .toOSString ());
131- return result ;
132- }
133-
134- JdtlsExtActivator .logInfo ("Found " + javaProjects .size () +
135- " Java sub-project(s) under: " + parentPath .toOSString ());
136-
137- // Mark as aggregator project
138- result .add (new DependencyInfo ("aggregatorProject" , "true" ));
139- result .add (new DependencyInfo ("totalSubProjects" , String .valueOf (javaProjects .size ())));
140-
141- // Collect sub-project names for reference
142- StringBuilder projectNames = new StringBuilder ();
143- for (int i = 0 ; i < javaProjects .size (); i ++) {
144- if (i > 0 ) projectNames .append (", " );
145- projectNames .append (javaProjects .get (i ).getProject ().getName ());
146- }
147- result .add (new DependencyInfo ("subProjectNames" , projectNames .toString ()));
148-
149- // Determine the primary/representative Java version (most common or highest)
150- String primaryJavaVersion = determinePrimaryJavaVersion (javaProjects );
151- if (primaryJavaVersion != null ) {
152- result .add (new DependencyInfo (KEY_JAVA_VERSION , primaryJavaVersion ));
153- }
154-
155- // Collect all unique libraries across sub-projects (top 10 most common)
156- Map <String , Integer > libraryFrequency = collectLibraryFrequency (javaProjects , monitor );
157- addTopLibraries (result , libraryFrequency , 10 );
158-
159- // Detect build tool from parent directory
160- IProject parentProject = findProjectByPath (root , parentPath );
161- if (parentProject != null ) {
162- detectBuildTool (result , parentProject );
163- }
164-
165- // Get JRE container info from first sub-project (usually consistent across modules)
166- if (!javaProjects .isEmpty ()) {
167- extractJreInfo (result , javaProjects .get (0 ));
168- }
169-
170- return result ;
171- }
172-
173- /**
174- * Determine the primary Java version from all sub-projects.
175- * Returns the most common version, or the highest if there's a tie.
176- */
177- private static String determinePrimaryJavaVersion (List <IJavaProject > javaProjects ) {
178- Map <String , Integer > versionCount = new ConcurrentHashMap <>();
179-
180- for (IJavaProject jp : javaProjects ) {
181- String version = jp .getOption (JavaCore .COMPILER_COMPLIANCE , true );
182- if (version != null ) {
183- versionCount .put (version , versionCount .getOrDefault (version , 0 ) + 1 );
184- }
185- }
186-
187- if (versionCount .isEmpty ()) {
188- return null ;
189- }
190-
191- // Find most common version (or highest if tie)
192- return versionCount .entrySet ().stream ()
193- .max ((e1 , e2 ) -> {
194- int countCompare = Integer .compare (e1 .getValue (), e2 .getValue ());
195- if (countCompare != 0 ) return countCompare ;
196- // If same count, prefer higher version
197- return e1 .getKey ().compareTo (e2 .getKey ());
198- })
199- .map (Map .Entry ::getKey )
200- .orElse (null );
201- }
202-
203- /**
204- * Collect frequency of all libraries across sub-projects.
205- * Returns a map of library name to frequency count.
206- */
207- private static Map <String , Integer > collectLibraryFrequency (
208- List <IJavaProject > javaProjects , IProgressMonitor monitor ) {
209103
210- Map <String , Integer > libraryFrequency = new ConcurrentHashMap <>();
211-
212- for (IJavaProject jp : javaProjects ) {
213- if (monitor .isCanceled ()) {
214- break ;
215- }
216-
217- try {
218- IClasspathEntry [] entries = jp .getResolvedClasspath (true );
219- for (IClasspathEntry entry : entries ) {
220- if (entry .getEntryKind () == IClasspathEntry .CPE_LIBRARY ) {
221- IPath libPath = entry .getPath ();
222- if (libPath != null ) {
223- String libName = libPath .lastSegment ();
224- libraryFrequency .put (libName ,
225- libraryFrequency .getOrDefault (libName , 0 ) + 1 );
226- }
227- }
228- }
229- } catch (JavaModelException e ) {
230- // Skip this project
104+ // First pass: check for exact project location match (most efficient)
105+ for (IProject p : allProjects ) {
106+ if (p .getLocation () != null && p .getLocation ().equals (filePath )) {
107+ return p ;
231108 }
232109 }
233110
234- return libraryFrequency ;
235- }
236-
237- /**
238- * Add top N most common libraries to result.
239- */
240- private static void addTopLibraries (List <DependencyInfo > result ,
241- Map <String , Integer > libraryFrequency , int topN ) {
242-
243- if (libraryFrequency .isEmpty ()) {
244- result .add (new DependencyInfo (KEY_TOTAL_LIBRARIES , "0" ));
245- return ;
246- }
247-
248- // Sort by frequency (descending) and take top N
249- List <Map .Entry <String , Integer >> topLibs = libraryFrequency .entrySet ().stream ()
250- .sorted ((e1 , e2 ) -> Integer .compare (e2 .getValue (), e1 .getValue ()))
251- .limit (topN )
252- .collect (java .util .stream .Collectors .toList ());
253-
254- result .add (new DependencyInfo (KEY_TOTAL_LIBRARIES ,
255- String .valueOf (libraryFrequency .size ())));
256-
257- // Add top common libraries
258- int index = 1 ;
259- for (Map .Entry <String , Integer > entry : topLibs ) {
260- result .add (new DependencyInfo ("commonLibrary_" + index ,
261- entry .getKey () + " (used in " + entry .getValue () + " modules)" ));
262- index ++;
263- }
264- }
265-
266- /**
267- * Extract JRE container information from a Java project.
268- */
269- private static void extractJreInfo (List <DependencyInfo > result , IJavaProject javaProject ) {
270- try {
271- IClasspathEntry [] entries = javaProject .getResolvedClasspath (true );
272- for (IClasspathEntry entry : entries ) {
273- if (entry .getEntryKind () == IClasspathEntry .CPE_CONTAINER ) {
274- String containerPath = entry .getPath ().toString ();
275- if (containerPath .contains ("JRE_CONTAINER" )) {
276- try {
277- String vmInstallName = JavaRuntime .getVMInstallName (entry .getPath ());
278- addIfNotNull (result , KEY_JRE_CONTAINER , vmInstallName );
279- return ;
280- } catch (Exception e ) {
281- // Fallback: extract from path
282- if (containerPath .contains ("JavaSE-" )) {
283- int startIdx = containerPath .lastIndexOf ("JavaSE-" );
284- String version = containerPath .substring (startIdx );
285- if (version .contains ("/" )) {
286- version = version .substring (0 , version .indexOf ("/" ));
287- }
288- result .add (new DependencyInfo (KEY_JRE_CONTAINER , version ));
289- return ;
290- }
291- }
292- }
293- }
294- }
295- } catch (JavaModelException e ) {
296- // Ignore
297- }
298- }
299-
300- /**
301- * Find project by path from all projects in workspace.
302- */
303- private static IProject findProjectByPath (IWorkspaceRoot root , IPath projectPath ) {
304- IProject [] allProjects = root .getProjects ();
111+ // Second pass: check if the file path is within any project directory
112+ // This handles cases where filePath points to a file or folder inside a project
305113 for (IProject p : allProjects ) {
306- if (p .getLocation () != null && p .getLocation ().equals ( projectPath )) {
114+ if (p .getLocation () != null && p .getLocation ().isPrefixOf ( filePath )) {
307115 return p ;
308116 }
309117 }
118+
310119 return null ;
311120 }
312121
0 commit comments