Skip to content

Commit

Permalink
Implement cache feature (see #159)
Browse files Browse the repository at this point in the history
  • Loading branch information
angelozerr committed Oct 12, 2018
1 parent ddce5ea commit e299f5a
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ private void updateSettings(ContentModelSettings settings) {
if (settings.getFileAssociations() != null) {
// Update XML file associations
ContentModelManager.getInstance().setFileAssociations(settings.getFileAssociations());
}
}
ContentModelManager.getInstance().setUseCache(settings.isUseCache());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.xs.XSLoaderImpl;
import org.apache.xerces.util.XMLCatalogResolver;
import org.apache.xerces.xs.XSModel;
Expand All @@ -24,10 +25,14 @@
import org.eclipse.lsp4xml.dom.SchemaLocation;
import org.eclipse.lsp4xml.dom.XMLDocument;
import org.eclipse.lsp4xml.extensions.contentmodel.settings.XMLFileAssociation;
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.extensions.contentmodel.xsd.XSDDocument;
import org.eclipse.lsp4xml.uriresolver.CacheResourceLoadingException;
import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager;
import org.w3c.dom.DOMError;
import org.w3c.dom.DOMErrorHandler;

/**
* Content model manager used to load XML Schema, DTD.
Expand All @@ -43,18 +48,33 @@ public static ContentModelManager getInstance() {

private final XSLoaderImpl loader;

private Map<String, CMDocument> cmDocumentCache;
private final Map<String, CMDocument> cmDocumentCache;

private final XMLCacheResolverExtension cacheResolverExtension;
private final XMLCatalogResolverExtension catalogResolverExtension;
private final XMLFileAssociationResolverExtension fileAssociationResolver;

public ContentModelManager() {
cmDocumentCache = new HashMap<>();
URIResolverExtensionManager resolverManager = URIResolverExtensionManager.getInstance();
loader = new XSLoaderImpl();
cmDocumentCache = new HashMap<>();
loader.setParameter("http://apache.org/xml/properties/internal/entity-resolver", resolverManager);
loader.setParameter(Constants.DOM_ERROR_HANDLER, new DOMErrorHandler() {

@Override
public boolean handleError(DOMError error) {
if (error.getRelatedException() instanceof CacheResourceLoadingException) {
throw ((CacheResourceLoadingException) error.getRelatedException());
}
return false;
}
});
cacheResolverExtension = new XMLCacheResolverExtension();
resolverManager.registerResolver(cacheResolverExtension);
fileAssociationResolver = new XMLFileAssociationResolverExtension();
URIResolverExtensionManager.getInstance().registerResolver(fileAssociationResolver);
resolverManager.registerResolver(fileAssociationResolver);
catalogResolverExtension = new XMLCatalogResolverExtension();
URIResolverExtensionManager.getInstance().registerResolver(catalogResolverExtension);
resolverManager.registerResolver(catalogResolverExtension);
}

public CMElementDeclaration findCMElement(Element element) throws Exception {
Expand Down Expand Up @@ -109,9 +129,6 @@ public CMDocument findCMDocument(XMLDocument xmlDocument, String namespaceURI) {
*/
private CMDocument findCMDocument(String uri, String publicId, String systemId) {
String key = URIResolverExtensionManager.getInstance().resolve(uri, publicId, systemId);
if (key == null) {
key = systemId;
}
if (key == null) {
return null;
}
Expand Down Expand Up @@ -170,4 +187,8 @@ public void setRootURI(String rootUri) {
fileAssociationResolver.setRootUri(rootUri);
}

public void setUseCache(boolean useCache) {
cacheResolverExtension.setUseCache(useCache);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.lsp4xml.services.extensions.CompletionParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.ICompletionRequest;
import org.eclipse.lsp4xml.services.extensions.ICompletionResponse;
import org.eclipse.lsp4xml.uriresolver.CacheResourceLoadingException;

/**
* Extension to support XML completion based on content model (XML Schema
Expand All @@ -36,35 +37,41 @@ public class ContentModelCompletionParticipant extends CompletionParticipantAdap

@Override
public void onTagOpen(ICompletionRequest request, ICompletionResponse response) throws Exception {
Element parentElement = request.getParentElement();
if (parentElement == null) {
// check if it's root element (in the case of XML file associations, the link to
// XML Schema is done with pattern and not with XML root element)
CMDocument cmDocument = ContentModelManager.getInstance().findCMDocument(request.getXMLDocument(), null);
if (cmDocument != null) {
fillWithChildrenElementDeclaration(cmDocument.getElements(), null, request, response);
}
return;
}
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(parentElement);
String defaultPrefix = null;
if (cmElement != null) {
defaultPrefix = parentElement.getPrefix();
fillWithChildrenElementDeclaration(cmElement.getElements(), defaultPrefix, request, response);
}
if (parentElement.isDocumentElement()) {
// root document element
Collection<String> prefixes = parentElement.getAllPrefixes();
for (String prefix : prefixes) {
if (defaultPrefix != null && prefix.equals(defaultPrefix)) {
continue;
}
String namespaceURI = parentElement.getNamespaceURI(prefix);
CMDocument cmDocument = ContentModelManager.getInstance().findCMDocument(parentElement, namespaceURI);
try {
Element parentElement = request.getParentElement();
if (parentElement == null) {
// check if it's root element (in the case of XML file associations, the link to
// XML Schema is done with pattern and not with XML root element)
CMDocument cmDocument = ContentModelManager.getInstance().findCMDocument(request.getXMLDocument(),
null);
if (cmDocument != null) {
fillWithChildrenElementDeclaration(cmDocument.getElements(), prefix, request, response);
fillWithChildrenElementDeclaration(cmDocument.getElements(), null, request, response);
}
return;
}
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(parentElement);
String defaultPrefix = null;
if (cmElement != null) {
defaultPrefix = parentElement.getPrefix();
fillWithChildrenElementDeclaration(cmElement.getElements(), defaultPrefix, request, response);
}
if (parentElement.isDocumentElement()) {
// root document element
Collection<String> prefixes = parentElement.getAllPrefixes();
for (String prefix : prefixes) {
if (defaultPrefix != null && prefix.equals(defaultPrefix)) {
continue;
}
String namespaceURI = parentElement.getNamespaceURI(prefix);
CMDocument cmDocument = ContentModelManager.getInstance().findCMDocument(parentElement,
namespaceURI);
if (cmDocument != null) {
fillWithChildrenElementDeclaration(cmDocument.getElements(), prefix, request, response);
}
}
}
} catch (CacheResourceLoadingException e) {
addCacheWarningItem(e, response);
}
}

Expand Down Expand Up @@ -94,51 +101,54 @@ public void onAttributeName(boolean generateValue, Range fullRange, ICompletionR
if (parentElement == null) {
return;
}
boolean canSupportSnippet = request.getCompletionSettings().isCompletionSnippetsSupported();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(parentElement);
if (cmElement != null) {
Collection<CMAttributeDeclaration> attributes = cmElement.getAttributes();
if (attributes != null) {
for (CMAttributeDeclaration cmAttribute : attributes) {
String attrName = cmAttribute.getName();
if (!parentElement.hasAttribute(attrName)) {
CompletionItem item = new CompletionItem();
item.setLabel(attrName);
item.setKind(CompletionItemKind.Unit);
StringBuilder attributeContent = new StringBuilder(attrName);
if (generateValue) {
attributeContent.append("=\"");
String defaultValue = cmAttribute.getDefaultValue();
if (defaultValue == null) {
if (canSupportSnippet) {
attributeContent.append("$1");
}
} else {
if (canSupportSnippet) {
attributeContent.append("${1:");
try {
boolean canSupportSnippet = request.getCompletionSettings().isCompletionSnippetsSupported();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(parentElement);
if (cmElement != null) {
Collection<CMAttributeDeclaration> attributes = cmElement.getAttributes();
if (attributes != null) {
for (CMAttributeDeclaration cmAttribute : attributes) {
String attrName = cmAttribute.getName();
if (!parentElement.hasAttribute(attrName)) {
CompletionItem item = new CompletionItem();
item.setLabel(attrName);
item.setKind(CompletionItemKind.Unit);
StringBuilder attributeContent = new StringBuilder(attrName);
if (generateValue) {
attributeContent.append("=\"");
String defaultValue = cmAttribute.getDefaultValue();
if (defaultValue == null) {
if (canSupportSnippet) {
attributeContent.append("$1");
}
} else {
if (canSupportSnippet) {
attributeContent.append("${1:");
}
attributeContent.append(defaultValue);
if (canSupportSnippet) {
attributeContent.append("}");
}
}
attributeContent.append(defaultValue);
attributeContent.append("\"");
if (canSupportSnippet) {
attributeContent.append("}");
attributeContent.append("$0");
}
}
attributeContent.append("\"");
if (canSupportSnippet) {
attributeContent.append("$0");
item.setTextEdit(new TextEdit(fullRange, attributeContent.toString()));
item.setInsertTextFormat(InsertTextFormat.Snippet);
String documentation = cmAttribute.getDocumentation();
if (documentation != null) {
item.setDetail(documentation);
}
response.addCompletionAttribute(item);
}
item.setTextEdit(new TextEdit(fullRange, attributeContent.toString()));
item.setInsertTextFormat(InsertTextFormat.Snippet);
String documentation = cmAttribute.getDocumentation();
if (documentation != null) {
item.setDetail(documentation);
}
response.addCompletionAttribute(item);
}
}
}
} catch (CacheResourceLoadingException e) {
addCacheWarningItem(e, response);
}

}

@Override
Expand All @@ -148,18 +158,31 @@ public void onAttributeValue(String valuePrefix, Range fullRange, boolean addQuo
if (parentElement == null) {
return;
}
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(parentElement);
if (cmElement != null) {
String attributeName = request.getCurrentAttributeName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);
if (cmAttribute != null) {
cmAttribute.getEnumerationValues().forEach(value -> {
CompletionItem item = new CompletionItem();
item.setLabel(value);
item.setKind(CompletionItemKind.Value);
response.addCompletionAttribute(item);
});
try {
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(parentElement);
if (cmElement != null) {
String attributeName = request.getCurrentAttributeName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);
if (cmAttribute != null) {
cmAttribute.getEnumerationValues().forEach(value -> {
CompletionItem item = new CompletionItem();
item.setLabel(value);
item.setKind(CompletionItemKind.Value);
response.addCompletionAttribute(item);
});
}
}
} catch (CacheResourceLoadingException e) {
addCacheWarningItem(e, response);
}
}

private void addCacheWarningItem(CacheResourceLoadingException e, ICompletionResponse response) {
// Here cache is enabled and some XML Schema, DTD, etc are loading
CompletionItem item = new CompletionItem(
"Cannot process " + (e.isDTD() ? "DTD" : "XML Schema") + " completion: " + e.getMessage());
item.setInsertText("");
response.addCompletionItem(item);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.services.extensions.HoverParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.IHoverRequest;
import org.eclipse.lsp4xml.uriresolver.CacheResourceLoadingException;

/**
* Extension to support XML hover based on content model (XML Schema completion,
Expand All @@ -29,38 +30,54 @@ public class ContentModelHoverParticipant extends HoverParticipantAdapter {

@Override
public Hover onTag(IHoverRequest hoverRequest) throws Exception {
Element node = (Element) hoverRequest.getNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(node);
if (cmElement != null) {
String doc = cmElement.getDocumentation();
if (doc != null && doc.length() > 0) {
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.PLAINTEXT);
content.setValue(doc);
return new Hover(content, hoverRequest.getTagRange());
try {
Element node = (Element) hoverRequest.getNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(node);
if (cmElement != null) {
String doc = cmElement.getDocumentation();
if (doc != null && doc.length() > 0) {
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.PLAINTEXT);
content.setValue(doc);
return new Hover(content, hoverRequest.getTagRange());
}
}
} catch (CacheResourceLoadingException e) {
return getCacheWarningHover(e);
}
return null;
}

@Override
public Hover onAttributeName(IHoverRequest hoverRequest) throws Exception {
Attr attribute = (Attr) hoverRequest.getNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(attribute.getOwnerElement());
if (cmElement != null) {
String attributeName = attribute.getName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);
if (cmAttribute != null) {
String doc = cmAttribute.getDocumentation();
if (doc != null && doc.length() > 0) {
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.PLAINTEXT);
content.setValue(doc);
return new Hover(content);
try {
Attr attribute = (Attr) hoverRequest.getNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance()
.findCMElement(attribute.getOwnerElement());
if (cmElement != null) {
String attributeName = attribute.getName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);
if (cmAttribute != null) {
String doc = cmAttribute.getDocumentation();
if (doc != null && doc.length() > 0) {
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.PLAINTEXT);
content.setValue(doc);
return new Hover(content);
}
}
}
} catch (CacheResourceLoadingException e) {
return getCacheWarningHover(e);
}
return null;
}

private Hover getCacheWarningHover(CacheResourceLoadingException e) {
// Here cache is enabled and some XML Schema, DTD, etc are loading
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.PLAINTEXT);
content.setValue("Cannot process " + (e.isDTD() ? "DTD" : "XML Schema") + " hover: " + e.getMessage());
return new Hover(content);
}
}
Loading

0 comments on commit e299f5a

Please sign in to comment.