diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java index e3d77e329..e416ebceb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java @@ -11,18 +11,25 @@ package org.springframework.ide.vscode.boot.java.utils; import java.io.File; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.eclipse.lsp4xml.dom.DOMDocument; import org.eclipse.lsp4xml.dom.DOMNode; import org.eclipse.lsp4xml.dom.DOMParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.UriUtil; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -61,9 +68,31 @@ public class SpringIndexerXML implements SpringIndexer { log.info("scan xml files for symbols for project: " + project.getElementName() + " - no. of files: " + files.length); long startTime = System.currentTimeMillis(); - for (String file : files) { - scanFile(project, file); + SymbolCacheKey cacheKey = getCacheKey(project); + + CachedSymbol[] symbols = this.cache.retrieve(cacheKey, files); + if (symbols == null) { + List generatedSymbols = new ArrayList(); + + for (String file : files) { + scanFile(project, file, generatedSymbols); + } + + this.cache.store(cacheKey, files, generatedSymbols); + + symbols = (CachedSymbol[]) generatedSymbols.toArray(new CachedSymbol[generatedSymbols.size()]); } + else { + log.info("scan xml files used cached data: " + project.getElementName() + " - no. of cached symbols retrieved: " + symbols.length); + } + + if (symbols != null) { + for (int i = 0; i < symbols.length; i++) { + CachedSymbol symbol = symbols[i]; + symbolHandler.addSymbol(project, symbol.getDocURI(), symbol.getEnhancedSymbol()); + } + } + long endTime = System.currentTimeMillis(); log.info("scan xml files for symbols for project: " + project.getElementName() + " took ms: " + (endTime - startTime)); @@ -71,18 +100,34 @@ public class SpringIndexerXML implements SpringIndexer { @Override public void removeProject(IJavaProject project) throws Exception { + SymbolCacheKey cacheKey = getCacheKey(project); + this.cache.remove(cacheKey); } @Override public void updateFile(IJavaProject project, String docURI, long lastModified, String content) throws Exception { - scanFile(project, content, docURI); + + List generatedSymbols = new ArrayList(); + + scanFile(project, content, docURI, lastModified, generatedSymbols); + + SymbolCacheKey cacheKey = getCacheKey(project); + String file = new File(new URI(docURI)).getAbsolutePath(); + this.cache.update(cacheKey, file, lastModified, generatedSymbols); + + for (CachedSymbol symbol : generatedSymbols) { + symbolHandler.addSymbol(project, symbol.getDocURI(), symbol.getEnhancedSymbol()); + } } @Override public void removeFile(IJavaProject project, String docURI) throws Exception { + SymbolCacheKey cacheKey = getCacheKey(project); + String file = new File(new URI(docURI)).getAbsolutePath(); + this.cache.removeFile(cacheKey, file); } - private void scanFile(IJavaProject project, String fileName) { + private void scanFile(IJavaProject project, String fileName, List generatedSymbols) { log.debug("starting to parse XML file for Spring symbol indexing: ", fileName); try { @@ -92,29 +137,29 @@ public class SpringIndexerXML implements SpringIndexer { String docURI = UriUtil.toUri(file).toString(); String fileContent = FileUtils.readFileToString(file); - scanFile(project, fileContent, docURI); + scanFile(project, fileContent, docURI, lastModified, generatedSymbols); } catch (Exception e) { log.error("error parsing XML file: ", e); } } - private void scanFile(IJavaProject project, String fileContent, String docURI) throws Exception { + private void scanFile(IJavaProject project, String fileContent, String docURI, long lastModified, List generatedSymbols) throws Exception { DOMParser parser = DOMParser.getInstance(); DOMDocument document = parser.parse(fileContent, "", null); AtomicReference docRef = new AtomicReference<>(); - scanNode(document, project, docURI, docRef, fileContent); + scanNode(document, project, docURI, lastModified, docRef, fileContent, generatedSymbols); } - private void scanNode(DOMNode node, IJavaProject project, String docURI, AtomicReference docRef, String content) throws Exception { + private void scanNode(DOMNode node, IJavaProject project, String docURI, long lastModified, AtomicReference docRef, String content, List generatedSymbols) throws Exception { String namespaceURI = node.getNamespaceURI(); if (namespaceURI != null && this.namespaceHandler.containsKey(namespaceURI)) { SpringIndexerXMLNamespaceHandler namespaceHandler = this.namespaceHandler.get(namespaceURI); TextDocument document = DocumentUtils.getTempTextDocument(docURI, docRef, content); - namespaceHandler.processNode(node, project, docURI, document, this.symbolHandler); + namespaceHandler.processNode(node, project, docURI, lastModified, document, generatedSymbols); } @@ -129,7 +174,7 @@ public class SpringIndexerXML implements SpringIndexer { List children = node.getChildren(); for (DOMNode child : children) { - scanNode(child, project, docURI, docRef, content); + scanNode(child, project, docURI, lastModified, docRef, content, generatedSymbols); } @@ -143,4 +188,16 @@ public class SpringIndexerXML implements SpringIndexer { .toArray(String[]::new); } + private SymbolCacheKey getCacheKey(IJavaProject project) { + IClasspath classpath = project.getClasspath(); + Stream classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream(); + + String classpathIdentifier = classpathEntries + .filter(file -> file.exists()) + .map(file -> file.getAbsolutePath() + "#" + file.lastModified()) + .collect(Collectors.joining(",")); + + return new SymbolCacheKey(project.getElementName() + "-xml-", DigestUtils.md5Hex(classpathIdentifier).toUpperCase()); + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java index ffcd5fd9e..478683854 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; +import java.util.List; + import org.eclipse.lsp4xml.dom.DOMNode; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -19,6 +21,6 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public interface SpringIndexerXMLNamespaceHandler { - void processNode(DOMNode node, IJavaProject project, String docURI, TextDocument document, SymbolHandler symbolHandler) throws Exception; + void processNode(DOMNode node, IJavaProject project, String docURI, long lastModifiued, TextDocument document, List generatedSymbols) throws Exception; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java index 50eaf0e67..7b40012f3 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java @@ -31,14 +31,14 @@ import org.springframework.lang.NonNull; public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNamespaceHandler { @Override - public void processNode(DOMNode node, IJavaProject project, String docURI, TextDocument document, SymbolHandler symbolHandler) throws Exception { + public void processNode(DOMNode node, IJavaProject project, String docURI, long lastModified, TextDocument document, List generatedSymbols) throws Exception { String localName = node.getLocalName(); if (localName != null && "bean".equals(localName)) { - createBeanSymbol(node, project, docURI, document, symbolHandler); + createBeanSymbol(node, project, docURI, lastModified, document, generatedSymbols); } } - private void createBeanSymbol(DOMNode node, IJavaProject project, String docURI, TextDocument document, SymbolHandler symbolHandler) throws Exception { + private void createBeanSymbol(DOMNode node, IJavaProject project, String docURI, long lastModified, TextDocument document, List generatedSymbols) throws Exception { String beanID = null; int symbolStart = 0; int symbolEnd = 0; @@ -88,7 +88,9 @@ public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNa SymbolInformation symbol = new SymbolInformation("@+ '" + beanID + "' " + beanClass, SymbolKind.Interface, new Location(docURI, range)); EnhancedSymbolInformation fullSymbol = new EnhancedSymbolInformation(symbol, null); - symbolHandler.addSymbol(project, docURI, fullSymbol); + + CachedSymbol cachedSymbol = new CachedSymbol(docURI, lastModified, fullSymbol); + generatedSymbols.add(cachedSymbol); } }