Skip to content

Commit

Permalink
Use DTD/XML Schema cache manager for completion
Browse files Browse the repository at this point in the history
This PR uses like validation the cache manager to load the DTD/XML
Schema content model used for completion based on grammar, hover. It
should fix the performance problem of #397

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr authored and fbricon committed May 29, 2019
1 parent 12f97c0 commit 1097abe
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
package org.eclipse.lsp4xml.extensions.contentmodel.model;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -22,6 +23,7 @@
import org.eclipse.lsp4xml.extensions.contentmodel.uriresolver.XMLCacheResolverExtension;
import org.eclipse.lsp4xml.extensions.contentmodel.uriresolver.XMLCatalogResolverExtension;
import org.eclipse.lsp4xml.extensions.contentmodel.uriresolver.XMLFileAssociationResolverExtension;
import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4xml.utils.URIUtils;

Expand Down Expand Up @@ -114,7 +116,23 @@ private CMDocument findCMDocument(String uri, String publicId, String systemId,
cmDocument = cmDocumentCache.get(key);
}
if (cmDocument == null) {
cmDocument = modelProvider.createCMDocument(key);
if (isCacheable && cacheResolverExtension.isUseCache()) {
// Try to load the DTD/XML Schema with the cache manager
try {
Path file = cacheResolverExtension.getCachedResource(key);
if (file != null) {
cmDocument = modelProvider.createCMDocument(file.toFile().getPath());
}
} catch (CacheResourceDownloadingException e) {
// the DTD/XML Schema is downloading
return null;
} catch (Exception e) {
// other error like network which is not available
cmDocument = modelProvider.createCMDocument(key);
}
} else {
cmDocument = modelProvider.createCMDocument(key);
}
if (isCacheable && cmDocument != null) {
cmDocumentCache.put(key, cmDocument);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadedException;
import org.eclipse.lsp4xml.uriresolver.CacheResourcesManager;
import org.eclipse.lsp4xml.uriresolver.URIResolverExtension;

Expand Down Expand Up @@ -44,18 +45,34 @@ public String resolve(String baseLocation, String publicId, String systemId) {
@Override
public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) throws XNIException, IOException {
String url = resourceIdentifier.getExpandedSystemId();
// Try to get the downloaded resource. In the case where the resource is
// downloading but takes too long, a CacheResourceDownloadingException is
// thrown.
Path file = getCachedResource(url);
if (file != null) {
// The resource was downloaded locally, use it.
XMLInputSource source = new XMLInputSource(resourceIdentifier);
source.setByteStream(Files.newInputStream(file));
return source;
}
return null;
}

/**
* Returns the cached resource path from the given url and null otherwise.
*
* @param url the url
* @return the cached resource path from the given url and null otherwise.
* @throws IOException
* @throws CacheResourceDownloadedException throws when resource is downloading.
*/
public Path getCachedResource(String url) throws IOException, CacheResourceDownloadedException {
// Cache is used only for resource coming from "http(s)" or "ftp".
if (cacheResourcesManager.canUseCache(url)) {
// Try to get the downloaded resource. In the case where the resource is
// downloading but takes too long, a CacheResourceDownloadingException is
// thrown.
Path file = cacheResourcesManager.getResource(url);
if (file != null) {
// The resource was downloaded locally, use it.
XMLInputSource source = new XMLInputSource(resourceIdentifier);
source.setByteStream(Files.newInputStream(file));
return source;
}
return cacheResourcesManager.getResource(url);
}
return null;
}
Expand All @@ -70,4 +87,15 @@ public void setUseCache(boolean useCache) {
cacheResourcesManager.setUseCache(useCache);
}

/**
* Returns <code>true</code> if cache must be used, <code>false</code>
* otherwise.
*
* @return <code>true</code> if cache must be used, <code>false</code>
* otherwise.
*/
public boolean isUseCache() {
return cacheResourcesManager.isUseCache();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2019 Red Hat, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*/
package org.eclipse.lsp4xml.extensions.contentmodel;

import static org.eclipse.lsp4xml.XMLAssert.c;
import static org.eclipse.lsp4xml.XMLAssert.te;

import java.io.FileOutputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;

import org.eclipse.lsp4xml.AbstractCacheBasedTest;
import org.eclipse.lsp4xml.XMLAssert;
import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.services.XMLLanguageService;
import org.junit.Test;

/**
* Test with DTD completion and cache.
*
* @author Angelo ZERR
*
*/
public class DTDCompletionWithCacheExtensionsTest extends AbstractCacheBasedTest {

@Test
public void dtdCache() throws Exception {
Consumer<XMLLanguageService> configuration = ls -> {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
// Use cache on file system
contentModelManager.setUseCache(true);
};

// Copy the svg.dtd in the cache folder
Path expectedLocation = TEST_WORK_DIRECTORY
.resolve("cache/http/www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
// Download resource in a temporary file
Files.createDirectories(expectedLocation.getParent());
Path path = Files.createFile(expectedLocation);

try (ReadableByteChannel rbc = Channels
.newChannel(DTDCompletionWithCacheExtensionsTest.class.getResourceAsStream("/dtd/svg.dtd"));
FileOutputStream fos = new FileOutputStream(path.toFile())) {
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
}

// Execute completion based on svg.dtd by using the cache manager
String fileURI = "test.xml";
String xml = "<?xml version=\"1.0\" standalone=\"no\" ?>\r\n" + //
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\r\n"
+ //
"<svg xmlns=\"http://www.w3.org/2000/svg\">\r\n" + //
" <animate attributeName=\"foo\">\r\n" + //
" |\r\n"; // <- completion";

XMLLanguageService ls = new XMLLanguageService();
XMLAssert.testCompletionFor(ls, xml, null, configuration, fileURI, null, true,
c("desc", te(4, 8, 4, 8, "<desc></desc>"), "desc"));
}

}

0 comments on commit 1097abe

Please sign in to comment.