Skip to content

Commit

Permalink
Cache completion based on XML Schema/DTD
Browse files Browse the repository at this point in the history
Fixes #547

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Jul 25, 2019
1 parent 23a4a81 commit 022b75e
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,11 @@ public interface CMDocument {
* @return the location of the type definition of the given node.
*/
LocationLink findTypeLocation(DOMNode node);

/**
* Returns true if the content model document is dirty and false otherwise.
*
* @return true if the content model document is dirty and false otherwise.
*/
boolean isDirty();
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ public CMDocument findCMDocument(DOMDocument xmlDocument, String namespaceURI) {
* Returns true if the given document is linked to the given grammar URI (XML
* Schema, DTD) and false otherwise.
*
* @param document the DOM document
* @param grammarURI the grammar URI
* @param document the DOM document
* @param grammarURI the grammar URI
* @return true if the given document is linked to the given grammar URI (XML
* Schema, DTD) and false otherwise.
*/
Expand All @@ -98,7 +98,8 @@ public boolean dependsOnGrammar(DOMDocument document, String grammarURI) {
return false;
}
ContentModelProvider modelProvider = getModelProviderByStandardAssociation(document, false);
String systemId = modelProvider != null ? modelProvider.getSystemId(document, document.getNamespaceURI()) : null;
String systemId = modelProvider != null ? modelProvider.getSystemId(document, document.getNamespaceURI())
: null;
String key = resolverManager.resolve(document.getDocumentURI(), null, systemId);
return grammarURI.equals(key);
}
Expand Down Expand Up @@ -130,36 +131,56 @@ private CMDocument findCMDocument(String uri, String publicId, String systemId,
if (modelProvider == null) {
return null;
}
CMDocument cmDocument = null;
boolean isCacheable = isCacheable(key);
if (isCacheable) {
cmDocument = cmDocumentCache.get(key);
// Try to get the document from the cache
CMDocument cmDocument = getCMDocumentFromCache(key);
if (cmDocument != null) {
return cmDocument;
}
if (cmDocument == null) {
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);
boolean isFileResource = URIUtils.isFileResource(uri);
if (!isFileResource && cacheResolverExtension.isUseCache()) {
// The DTD/XML Schema comes from http://, ftp:// etc and cache manager is
// activated
// 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());
}
} else {
} 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);
}
if (isCacheable && cmDocument != null) {
cmDocumentCache.put(key, cmDocument);
}
} else {
cmDocument = modelProvider.createCMDocument(key);
}
// Cache the document
if (cmDocument != null) {
cache(key, cmDocument);
}
return cmDocument;
}

private CMDocument getCMDocumentFromCache(String key) {
CMDocument document = null;
synchronized (cmDocumentCache) {
document = cmDocumentCache.get(key);
if (document != null && document.isDirty()) {
cmDocumentCache.remove(key);
return null;
}
}
return document;
}

private void cache(String key, CMDocument cmDocument) {
synchronized (cmDocumentCache) {
cmDocumentCache.put(key, cmDocument);
}
}

public CMElementDeclaration findInternalCMElement(DOMElement element) throws Exception {
return findInternalCMElement(element, element.getNamespaceURI());
}
Expand Down Expand Up @@ -217,10 +238,6 @@ private ContentModelProvider getModelProviderByURI(String uri) {
return null;
}

private boolean isCacheable(String uri) {
return !URIUtils.isFileResource(uri);
}

/**
* Set up XML catalogs.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2019 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* 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.model;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* File changed tracker.
*
* @author Angelo ZERR
*
*/
public class FilesChangedTracker {

private static final Logger LOGGER = Logger.getLogger(FilesChangedTracker.class.getName());

private static class FileChangedTracker {

private final Path file;
private FileTime lastModified;

public FileChangedTracker(Path file) {
this.file = file;
try {
lastModified = Files.getLastModifiedTime(file);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Get last modified time failed", e);
}
}

public boolean isDirty() {
try {
FileTime currentLastMofied = Files.getLastModifiedTime(file);
if (!currentLastMofied.equals(lastModified)) {
lastModified = currentLastMofied;
return true;
}
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Get last modified time failed", e);
}
return false;
}

}

private final List<FileChangedTracker> files;

public FilesChangedTracker() {
files = new ArrayList<>();
}

/**
* Add file URI to track
*
* @param fileURI
*/
public void addFileURI(String fileURI) {
try {
files.add(new FileChangedTracker(Paths.get(new URI(fileURI))));
} catch (URISyntaxException e) {
LOGGER.log(Level.SEVERE, "Add file URI to track failed", e);
}
}

/**
* Returns true if one file has changed and false otherwise.
*
* @return true if one file has changed and false otherwise.
*/
public boolean isDirty() {
for (FileChangedTracker dirtyFile : files) {
if (dirtyFile.isDirty()) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -32,22 +33,27 @@
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMDocument;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.FilesChangedTracker;
import org.eclipse.lsp4xml.utils.URIUtils;

/**
* DTD document.
*
* @author azerr
* @author Angelo ZERR
*
*/
public class CMDTDDocument extends XMLDTDLoader implements CMDocument {

private final String uri;

private Map<String, Set<String>> hierarchiesMap;
private List<CMElementDeclaration> elements;
private DTDGrammar grammar;
private Set<String> hierarchies;
private String uri;
private FilesChangedTracker tracker;

public CMDTDDocument() {
this(null);
}

public CMDTDDocument(String uri) {
Expand Down Expand Up @@ -139,9 +145,37 @@ public void endContentModel(Augmentations augs) throws XNIException {
@Override
public Grammar loadGrammar(XMLInputSource source) throws IOException, XNIException {
grammar = (DTDGrammar) super.loadGrammar(source);
this.tracker = new FilesChangedTracker();
updateFilesChangedTracker();
return grammar;
}

/**
* Update files tracker by adding DTD
*/
private void updateFilesChangedTracker() {
Set<DTDGrammar> trackedGrammars = new HashSet<>();
updateTracker(grammar, trackedGrammars, tracker);
}

private static void updateTracker(DTDGrammar grammar, Set<DTDGrammar> trackedGrammars,
FilesChangedTracker tracker) {
if (grammar == null || trackedGrammars.contains(grammar)) {
return;
}
trackedGrammars.add(grammar);
// Track the grammar
String dtdURI = getDTDURI(grammar);
if (dtdURI != null && URIUtils.isFileResource(dtdURI)) {
// The DTD is a file, track when file changed
tracker.addFileURI(dtdURI);
}
}

private static String getDTDURI(DTDGrammar grammar) {
return grammar.getGrammarDescription().getExpandedSystemId();
}

public void loadInternalDTD(String internalSubset, String baseSystemId, String systemId)
throws XNIException, IOException {
// Load empty DTD grammar
Expand Down Expand Up @@ -189,4 +223,9 @@ void collectAttributesDeclaration(CMDTDElementDeclaration elementDecl, List<CMAt
public LocationLink findTypeLocation(DOMNode node) {
return null;
}

@Override
public boolean isDirty() {
return tracker != null ? tracker.isDirty() : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -49,6 +52,7 @@
import org.eclipse.lsp4xml.dom.DOMParser;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMDocument;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.FilesChangedTracker;
import org.eclipse.lsp4xml.extensions.xsd.utils.XSDUtils;
import org.eclipse.lsp4xml.utils.IOUtils;
import org.eclipse.lsp4xml.utils.StringUtils;
Expand All @@ -68,20 +72,53 @@ public class CMXSDDocument implements CMDocument, XSElementDeclHelper {

private final XSModel model;

private final String uri;

private final Map<XSElementDeclaration, CMXSDElementDeclaration> elementMappings;

private Collection<CMElementDeclaration> elements;

private String uri;
private final FilesChangedTracker tracker;

public CMXSDDocument(XSModel model) {
public CMXSDDocument(XSModel model, String uri) {
this.model = model;
this.elementMappings = new HashMap<>();
this.uri = uri;
this.tracker = new FilesChangedTracker();
updateFilesChangedTracker();
}

public CMXSDDocument(XSModel model, String uri) {
this(model);
this.uri = uri;
/**
* Update files tracker by adding all XML Schema (root and imported)
*/
private void updateFilesChangedTracker() {
Set<SchemaGrammar> trackedGrammars = new HashSet<>();
XSNamespaceItemList grammars = model.getNamespaceItems();
for (int i = 0; i < grammars.getLength(); i++) {
SchemaGrammar grammar = getSchemaGrammar(grammars.item(i));
updateTracker(grammar, trackedGrammars, tracker);
}
}

private static void updateTracker(SchemaGrammar grammar, Set<SchemaGrammar> trackedGrammars,
FilesChangedTracker tracker) {
if (grammar == null || trackedGrammars.contains(grammar)) {
return;
}
trackedGrammars.add(grammar);
// Track the grammar
String schemaURI = getSchemaURI(grammar);
if (schemaURI != null && URIUtils.isFileResource(schemaURI)) {
// The schema is a file, track when file changed
tracker.addFileURI(schemaURI);
}
// Track the imported grammars
Vector importedGrammars = grammar.getImportedGrammars();
if (importedGrammars != null) {
for (Object importedGrammar : importedGrammars) {
updateTracker((SchemaGrammar) importedGrammar, trackedGrammars, tracker);
}
}
}

@Override
Expand Down Expand Up @@ -554,4 +591,9 @@ private static LocationLink findXSAttribute(DOMAttr originAttribute, NodeList ch
}
return null;
}

@Override
public boolean isDirty() {
return tracker.isDirty();
}
}

0 comments on commit 022b75e

Please sign in to comment.