Skip to content

Commit 53cc57f

Browse files
committed
GH-1529: factories indexer now creates bean index elements as well
1 parent 20b3bfa commit 53cc57f

File tree

5 files changed

+251
-81
lines changed

5 files changed

+251
-81
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,10 @@ public void removeSymbols(IJavaProject project, String docURI) {
251251
springIndexerJava = new SpringIndexerJava(handler, specificProviders, this.cache, projectFinder(), server.getProgressService(), jdtReconciler, problemCollectorFactory, config.getJavaValidationSettingsJson(), cuCache);
252252
factoriesIndexer = new SpringFactoriesIndexer(handler, cache);
253253

254-
this.indexers = new SpringIndexer[] {springIndexerJava, factoriesIndexer};
254+
this.indexers = new SpringIndexer[] {
255+
factoriesIndexer, // factories indexder should be the first, so that java indexers and reconcilers can rely on those indexed elements
256+
springIndexerJava
257+
};
255258

256259
getWorkspaceService().onDidChangeWorkspaceFolders(evt -> {
257260
log.debug("workspace roots have changed event arrived - added: " + evt.getEvent().getAdded() + " - removed: " + evt.getEvent().getRemoved());

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java

+153-79
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Arrays;
2424
import java.util.Collections;
2525
import java.util.List;
26+
import java.util.Map;
2627
import java.util.Set;
2728
import java.util.stream.Collectors;
2829
import java.util.stream.Stream;
@@ -41,9 +42,13 @@
4142
import org.springframework.ide.vscode.boot.index.cache.IndexCacheKey;
4243
import org.springframework.ide.vscode.boot.java.beans.BeanUtils;
4344
import org.springframework.ide.vscode.boot.java.beans.BeansIndexer;
45+
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
4446
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
4547
import org.springframework.ide.vscode.commons.java.IJavaProject;
4648
import 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;
4752
import org.springframework.ide.vscode.commons.util.text.LanguageId;
4853
import org.springframework.ide.vscode.commons.util.text.Region;
4954
import 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

Comments
 (0)