2323import java .util .Arrays ;
2424import java .util .Collections ;
2525import java .util .List ;
26+ import java .util .Map ;
2627import java .util .Set ;
2728import java .util .stream .Collectors ;
2829import java .util .stream .Stream ;
4142import org .springframework .ide .vscode .boot .index .cache .IndexCacheKey ;
4243import org .springframework .ide .vscode .boot .java .beans .BeanUtils ;
4344import org .springframework .ide .vscode .boot .java .beans .BeansIndexer ;
45+ import org .springframework .ide .vscode .boot .java .beans .CachedBean ;
4446import org .springframework .ide .vscode .commons .java .IClasspathUtil ;
4547import org .springframework .ide .vscode .commons .java .IJavaProject ;
4648import org .springframework .ide .vscode .commons .protocol .java .Classpath ;
49+ import org .springframework .ide .vscode .commons .protocol .spring .Bean ;
50+ import org .springframework .ide .vscode .commons .protocol .spring .DefaultValues ;
51+ import org .springframework .ide .vscode .commons .protocol .spring .SpringIndexElement ;
4752import org .springframework .ide .vscode .commons .util .text .LanguageId ;
4853import org .springframework .ide .vscode .commons .util .text .Region ;
4954import org .springframework .ide .vscode .commons .util .text .TextDocument ;
@@ -61,6 +66,9 @@ public class SpringFactoriesIndexer implements SpringIndexer {
6166 // we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found
6267 private static final String GENERATION = "GEN-12" ;
6368
69+ private static final String SYMBOL_KEY = "symbols" ;
70+ private static final String BEANS_KEY = "beans" ;
71+
6472 private static final String FILE_PATTERN = "**/META-INF/spring/*.factories" ;
6573
6674 private static final PathMatcher FILE_GLOB_PATTERN = FileSystems .getDefault ().getPathMatcher ("glob:" + FILE_PATTERN );
@@ -99,12 +107,12 @@ public boolean isInterestedIn(String resource) {
99107 @ Override
100108 public List <WorkspaceSymbol > computeSymbols (IJavaProject project , String docURI , String content )
101109 throws Exception {
102- return computeSymbols (docURI , content );
110+ return computeSymbols (docURI , content ). symbols ;
103111 }
104112
105113 @ Override
106114 public List <DocumentSymbol > computeDocumentSymbols (IJavaProject project , String docURI , String content ) throws Exception {
107- return computeSymbols (docURI , content ).stream ()
115+ return computeSymbols (docURI , content ).symbols . stream ()
108116 .map (workspaceSymbol -> convertToDocumentSymbol (workspaceSymbol ))
109117 .toList ();
110118 }
@@ -124,8 +132,10 @@ else if (location.isRight()) {
124132 return new DocumentSymbol (workspaceSymbol .getName (), workspaceSymbol .getKind (), range , range );
125133 }
126134
127- private List < WorkspaceSymbol > computeSymbols (String docURI , String content ) {
135+ private ComputeResult computeSymbols (String docURI , String content ) {
128136 ImmutableList .Builder <WorkspaceSymbol > symbols = ImmutableList .builder ();
137+ ImmutableList .Builder <Bean > beans = ImmutableList .builder ();
138+
129139 PropertiesAst ast = new AntlrParser ().parse (content ).ast ;
130140 if (ast != null ) {
131141 for (KeyValuePair pair : ast .getPropertyValuePairs ()) {
@@ -141,18 +151,26 @@ private List<WorkspaceSymbol> computeSymbols(String docURI, String content) {
141151 String simpleName = getSimpleName (fqName );
142152 String beanId = BeanUtils .getBeanNameFromType (simpleName );
143153 Range range = doc .toRange (new Region (pair .getOffset (), pair .getLength ()));
144- symbols .add (new WorkspaceSymbol (
154+ Location location = new Location (docURI , range );
155+
156+ WorkspaceSymbol symbol = new WorkspaceSymbol (
145157 BeansIndexer .beanLabel (false , beanId , fqName , Paths .get (URI .create (docURI )).getFileName ().toString ()),
146158 SymbolKind .Interface ,
147- Either .forLeft (new Location (docURI , range ))));
159+ Either .forLeft (location ));
160+
161+ symbols .add (symbol );
162+
163+ Bean bean = new Bean (beanId , fqName , location , DefaultValues .EMPTY_INJECTION_POINTS , Collections .emptySet (), DefaultValues .EMPTY_ANNOTATIONS , false , symbol .getName ());
164+ beans .add (bean );
165+
148166 } catch (Exception e ) {
149167 log .error ("" , e );
150168 }
151169 }
152170 }
153171 }
154172 }
155- return symbols .build ();
173+ return new ComputeResult ( symbols .build (), beans . build () );
156174 }
157175
158176 private static String getSimpleName (String fqName ) {
@@ -163,22 +181,6 @@ private static String getSimpleName(String fqName) {
163181 return fqName ;
164182 }
165183
166- private IndexCacheKey getCacheKey (IJavaProject project ) {
167- String filesIndentifier = getFiles (project ).stream ()
168- .filter (f -> Files .isRegularFile (f ))
169- .map (f -> {
170- try {
171- return f .toAbsolutePath ().toString () + "#" + Files .getLastModifiedTime (f ).toMillis ();
172- } catch (IOException e ) {
173- log .error ("" , e );
174- return f .toAbsolutePath ().toString () + "#0" ;
175- }
176- })
177- .collect (Collectors .joining ("," ));
178- return new IndexCacheKey (project .getElementName (), "factories" , "" , DigestUtils .md5Hex (GENERATION + "-" + filesIndentifier ).toUpperCase ());
179- }
180-
181-
182184 @ Override
183185 public void initializeProject (IJavaProject project , boolean clean ) throws Exception {
184186 long startTime = System .currentTimeMillis ();
@@ -187,27 +189,39 @@ public void initializeProject(IJavaProject project, boolean clean) throws Except
187189
188190 log .info ("scan factories files for symbols for project: " + project .getElementName () + " - no. of files: " + files .size ());
189191
190- IndexCacheKey cacheKey = getCacheKey (project );
192+ IndexCacheKey symbolsCacheKey = getCacheKey (project , SYMBOL_KEY );
193+ IndexCacheKey beansCacheKey = getCacheKey (project , BEANS_KEY );
191194
192- CachedSymbol [] symbols = this .cache .retrieveSymbols (cacheKey , filesStr , CachedSymbol .class );
193- if (symbols == null || clean ) {
195+ CachedSymbol [] symbols = this .cache .retrieveSymbols (symbolsCacheKey , filesStr , CachedSymbol .class );
196+ CachedBean [] beans = this .cache .retrieveSymbols (beansCacheKey , filesStr , CachedBean .class );
197+
198+ if (symbols == null || beans == null || clean ) {
194199 List <CachedSymbol > generatedSymbols = new ArrayList <CachedSymbol >();
200+ List <CachedBean > generatedBeans = new ArrayList <CachedBean >();
195201
196202 for (Path file : files ) {
197- generatedSymbols .addAll (scanFile (file ));
203+ ScanResult scanResult = scanFile (file );
204+
205+ if (scanResult != null ) {
206+ generatedSymbols .addAll (scanResult .symbols );
207+ generatedBeans .addAll (scanResult .beans );
208+ }
198209 }
199210
200- this .cache .store (cacheKey , filesStr , generatedSymbols , null , CachedSymbol .class );
211+ this .cache .store (symbolsCacheKey , filesStr , generatedSymbols , null , CachedSymbol .class );
212+ this .cache .store (beansCacheKey , filesStr , generatedBeans , null , CachedBean .class );
201213
202214 symbols = (CachedSymbol []) generatedSymbols .toArray (new CachedSymbol [generatedSymbols .size ()]);
215+ beans = (CachedBean []) generatedBeans .toArray (new CachedBean [generatedBeans .size ()]);
203216 }
204217 else {
205218 log .info ("scan factories files used cached data: " + project .getElementName () + " - no. of cached symbols retrieved: " + symbols .length );
206219 }
207220
208- if (symbols != null ) {
221+ if (symbols != null && beans != null ) {
209222 WorkspaceSymbol [] enhancedSymbols = Arrays .stream (symbols ).map (cachedSymbol -> cachedSymbol .getEnhancedSymbol ()).toArray (WorkspaceSymbol []::new );
210- symbolHandler .addSymbols (project , enhancedSymbols , null , null );
223+ Map <String , List <SpringIndexElement >> beansByDoc = Arrays .stream (beans ).filter (cachedBean -> cachedBean .getBean () != null ).collect (Collectors .groupingBy (CachedBean ::getDocURI , Collectors .mapping (CachedBean ::getBean , Collectors .toList ())));
224+ symbolHandler .addSymbols (project , enhancedSymbols , beansByDoc , null );
211225 }
212226
213227 long endTime = System .currentTimeMillis ();
@@ -216,50 +230,13 @@ public void initializeProject(IJavaProject project, boolean clean) throws Except
216230
217231 }
218232
219- private List <CachedSymbol > scanFile (Path file ) {
220- try {
221- String content = Files .readString (file );
222- ImmutableList .Builder <CachedSymbol > builder = ImmutableList .builder ();
223- long lastModified = Files .getLastModifiedTime (file ).toMillis ();
224- String docUri = file .toUri ().toASCIIString ();
225- for (WorkspaceSymbol s : computeSymbols (docUri , content )) {
226- builder .add (new CachedSymbol (docUri , lastModified , s ));
227- }
228- return builder .build ();
229- } catch (IOException e ) {
230- log .error ("" , e );
231- return Collections .emptyList ();
232- }
233- }
234-
235- private List <Path > getFiles (IJavaProject project ) {
236- try {
237- return project .getClasspath ().getClasspathEntries ().stream ()
238- .filter (Classpath ::isProjectSource )
239- .map (cpe -> new File (cpe .getPath ()).toPath ())
240- .map (p -> p .resolve ("META-INF" ).resolve ("spring" ))
241- .filter (Files ::isDirectory )
242- .flatMap (d -> {
243- try {
244- return Files .list (d );
245- } catch (IOException e ) {
246- // ignore
247- return Stream .empty ();
248- }
249- })
250- .filter (p -> p .toString ().endsWith (".factories" ))
251- .collect (Collectors .toList ());
252- } catch (Exception e ) {
253- log .error ("" , e );
254- return Collections .emptyList ();
255- }
256-
257- }
258-
259233 @ Override
260234 public void removeProject (IJavaProject project ) throws Exception {
261- IndexCacheKey cacheKey = getCacheKey (project );
262- this .cache .remove (cacheKey );
235+ IndexCacheKey symbolsCacheKey = getCacheKey (project , SYMBOL_KEY );
236+ IndexCacheKey beansCacheKey = getCacheKey (project , BEANS_KEY );
237+
238+ this .cache .remove (symbolsCacheKey );
239+ this .cache .remove (beansCacheKey );
263240 }
264241
265242 @ Override
@@ -269,30 +246,53 @@ public void updateFile(IJavaProject project, DocumentDescriptor updatedDoc, Stri
269246 List <Path > outputFolders = IClasspathUtil .getOutputFolders (project .getClasspath ()).map (f -> f .toPath ()).collect (Collectors .toList ());
270247 String docURI = updatedDoc .getDocURI ();
271248 Path path = Paths .get (URI .create (docURI ));
249+
272250 if (!outputFolders .stream ().anyMatch (out -> path .startsWith (out ))) {
273- List <CachedSymbol > generatedSymbols = scanFile (path );
251+
252+ ScanResult scanResult = scanFile (path );
253+
254+ List <CachedSymbol > generatedSymbols = Collections .emptyList ();
255+ List <CachedBean > generatedBeans = Collections .emptyList ();
256+
257+ if (scanResult != null ) {
258+ generatedSymbols = scanResult .symbols ;
259+ generatedBeans = scanResult .beans ;
260+ }
261+
262+ IndexCacheKey symbolsCacheKey = getCacheKey (project , SYMBOL_KEY );
263+ IndexCacheKey beansCacheKey = getCacheKey (project , BEANS_KEY );
274264
275- IndexCacheKey cacheKey = getCacheKey (project );
276265 String file = new File (new URI (docURI )).getAbsolutePath ();
277- this .cache .update (cacheKey , file , updatedDoc .getLastModified (), generatedSymbols , null , CachedSymbol .class );
266+ this .cache .update (symbolsCacheKey , file , updatedDoc .getLastModified (), generatedSymbols , null , CachedSymbol .class );
267+ this .cache .update (beansCacheKey , file , updatedDoc .getLastModified (), generatedBeans , null , CachedBean .class );
278268
279269 WorkspaceSymbol [] symbols = generatedSymbols .stream ().map (cachedSymbol -> cachedSymbol .getEnhancedSymbol ()).toArray (WorkspaceSymbol []::new );
280- symbolHandler .addSymbols (project , docURI , symbols , null , null );
270+ List <SpringIndexElement > beans = generatedBeans .stream ().filter (cachedBean -> cachedBean .getBean () != null ).map (cachedBean -> cachedBean .getBean ()).toList ();
271+ symbolHandler .addSymbols (project , docURI , symbols , beans , null );
281272 }
282273 }
283274
284275 @ Override
285276 public void updateFiles (IJavaProject project , DocumentDescriptor [] updatedDocs ) throws Exception {
286- IndexCacheKey key = getCacheKey (project );
277+ IndexCacheKey symbolsCacheKey = getCacheKey (project , SYMBOL_KEY );
278+ IndexCacheKey beansCacheKey = getCacheKey (project , BEANS_KEY );
279+
287280 List <Path > outputFolders = IClasspathUtil .getOutputFolders (project .getClasspath ()).map (f -> f .toPath ()).collect (Collectors .toList ());
281+
288282 for (DocumentDescriptor d : updatedDocs ) {
289283 Path path = Paths .get (URI .create (d .getDocURI ()));
284+
290285 if (!outputFolders .stream ().anyMatch (out -> path .startsWith (out ))) {
286+
291287 if (Files .isRegularFile (path )) {
288+
292289 updateFile (project , d , Files .readString (path ));
290+
293291 } else {
294292 String file = new File (new URI (d .getDocURI ())).getAbsolutePath ();
295- cache .removeFile (key , file , CachedSymbol .class );
293+
294+ cache .removeFile (symbolsCacheKey , file , CachedSymbol .class );
295+ cache .removeFile (beansCacheKey , file , CachedBean .class );
296296 }
297297 }
298298 }
@@ -308,8 +308,82 @@ public void removeFiles(IJavaProject project, String[] docURIs) throws Exception
308308 }
309309 }).toArray (String []::new );
310310
311- IndexCacheKey key = getCacheKey (project );
312- cache .removeFiles (key , files , CachedSymbol .class );
311+ IndexCacheKey symbolsCacheKey = getCacheKey (project , SYMBOL_KEY );
312+ IndexCacheKey beansCacheKey = getCacheKey (project , SYMBOL_KEY );
313+
314+ cache .removeFiles (symbolsCacheKey , files , CachedSymbol .class );
315+ cache .removeFiles (beansCacheKey , files , CachedBean .class );
316+ }
317+
318+ private ScanResult scanFile (Path file ) {
319+ try {
320+
321+ String content = Files .readString (file );
322+
323+ ImmutableList .Builder <CachedSymbol > symbols = ImmutableList .builder ();
324+ ImmutableList .Builder <CachedBean > beans = ImmutableList .builder ();
325+
326+ long lastModified = Files .getLastModifiedTime (file ).toMillis ();
327+ String docUri = file .toUri ().toASCIIString ();
328+
329+ ComputeResult result = computeSymbols (docUri , content );
330+
331+ for (WorkspaceSymbol symbol : result .symbols ) {
332+ symbols .add (new CachedSymbol (docUri , lastModified , symbol ));
333+ }
334+
335+ for (Bean bean : result .beans ) {
336+ beans .add (new CachedBean (docUri , bean ));
337+ }
338+
339+ return new ScanResult (symbols .build (), beans .build ());
340+
341+ } catch (IOException e ) {
342+ log .error ("" , e );
343+ return null ;
344+ }
345+ }
346+
347+ private List <Path > getFiles (IJavaProject project ) {
348+ try {
349+ return project .getClasspath ().getClasspathEntries ().stream ()
350+ .filter (Classpath ::isProjectSource )
351+ .map (cpe -> new File (cpe .getPath ()).toPath ())
352+ .map (p -> p .resolve ("META-INF" ).resolve ("spring" ))
353+ .filter (Files ::isDirectory )
354+ .flatMap (d -> {
355+ try {
356+ return Files .list (d );
357+ } catch (IOException e ) {
358+ // ignore
359+ return Stream .empty ();
360+ }
361+ })
362+ .filter (p -> p .toString ().endsWith (".factories" ))
363+ .collect (Collectors .toList ());
364+ } catch (Exception e ) {
365+ log .error ("" , e );
366+ return Collections .emptyList ();
367+ }
368+
313369 }
314370
371+ private IndexCacheKey getCacheKey (IJavaProject project , String elementType ) {
372+ String filesIndentifier = getFiles (project ).stream ()
373+ .filter (f -> Files .isRegularFile (f ))
374+ .map (f -> {
375+ try {
376+ return f .toAbsolutePath ().toString () + "#" + Files .getLastModifiedTime (f ).toMillis ();
377+ } catch (IOException e ) {
378+ log .error ("" , e );
379+ return f .toAbsolutePath ().toString () + "#0" ;
380+ }
381+ })
382+ .collect (Collectors .joining ("," ));
383+ return new IndexCacheKey (project .getElementName (), "factories" , elementType , DigestUtils .md5Hex (GENERATION + "-" + filesIndentifier ).toUpperCase ());
384+ }
385+
386+ private static record ScanResult (List <CachedSymbol > symbols , List <CachedBean > beans ) {}
387+ private static record ComputeResult (List <WorkspaceSymbol > symbols , List <Bean > beans ) {}
388+
315389}
0 commit comments