Skip to content

Commit

Permalink
Use Xerces native API instead of JAXB + manage catalog + file
Browse files Browse the repository at this point in the history
assocoiation with URI resolver.
  • Loading branch information
angelozerr committed Sep 4, 2018
1 parent e61b56e commit 2ccd125
Show file tree
Hide file tree
Showing 28 changed files with 13,534 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,17 @@ public void didSave(DidSaveTextDocumentParams params) {
}

private void triggerValidation(TextDocument document, int version) {
if (future != null) {
if (future != null && !future.isCancelled()) {
future.cancel(true);
}
if (monitor != null) {
monitor.setCanceled(true);
}
monitor = new BasicCancelChecker();
triggerValidation(document, version, monitor);
}

private void triggerValidation(TextDocument document, int version, BasicCancelChecker monitor) {
future = xmlLanguageServer.schedule(() -> {
TextDocument currDocument = getDocument(document.getUri());
if (currDocument != null && currDocument.getVersion() == version) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*/
package org.eclipse.lsp4xml.contentmodel.model;

import java.util.Collection;

import org.eclipse.lsp4xml.model.Node;

/**
Expand All @@ -18,6 +20,8 @@
*/
public interface CMDocument {

Collection<CMElementDeclaration> getElements();

/**
* Returns the declared element which matches the given XML element and null
* otherwise.
Expand All @@ -26,6 +30,6 @@ public interface CMDocument {
* @return the declared element which matches the given XML element and null
* otherwise.
*/
CMElementDeclaration findCMElement(Node element);
CMElementDeclaration findCMElement(Node element, String namespace);

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public interface CMElementDeclaration {
*/
String getName();

/**
* Returns the declared element name with the given prefix.
*
* @return the declared element name with the given prefix.
*/
String getName(String prefix);

/**
* Returns the attributes of this declared element.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,18 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.xerces.impl.xs.XSLoaderImpl;
import org.apache.xerces.util.XMLCatalogResolver;
import org.apache.xerces.xs.XSModel;
import org.apache.xml.resolver.CatalogManager;
import org.apache.xml.resolver.tools.CatalogResolver;
import org.eclipse.lsp4xml.contentmodel.settings.XMLFileAssociation;
import org.eclipse.lsp4xml.contentmodel.uriresolver.XMLCatalogResolverExtension;
import org.eclipse.lsp4xml.contentmodel.uriresolver.XMLFileAssociationResolverExtension;
import org.eclipse.lsp4xml.contentmodel.xsd.XSDDocument;
import org.eclipse.lsp4xml.model.Node;
import org.eclipse.lsp4xml.model.Element;
import org.eclipse.lsp4xml.model.NoNamespaceSchemaLocation;
import org.eclipse.lsp4xml.model.SchemaLocation;
import org.eclipse.lsp4xml.model.XMLDocument;
import org.xml.sax.SAXException;
import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager;

/**
* Content model manager used to load XML Schema, DTD.
Expand All @@ -47,15 +45,21 @@ public static ContentModelManager getInstance() {

private Map<String, CMDocument> cmDocumentCache;

private CatalogResolver catalogResolver;

private XMLFileAssociation[] fileAssociations;
private final XMLCatalogResolverExtension catalogResolverExtension;
private final XMLFileAssociationResolverExtension fileAssociationResolver;

public ContentModelManager() {
loader = new XSLoaderImpl();
cmDocumentCache = new HashMap<>();
fileAssociationResolver = new XMLFileAssociationResolverExtension();
URIResolverExtensionManager.getInstance().registerResolver(fileAssociationResolver);
catalogResolverExtension = new XMLCatalogResolverExtension();
URIResolverExtensionManager.getInstance().registerResolver(catalogResolverExtension);
}

public CMElementDeclaration findCMElement(Element element) throws Exception {
return findCMElement(element, element.getNamespaceURI());
}
/**
* Returns the declared element which matches the given XML element and null
* otherwise.
Expand All @@ -64,24 +68,27 @@ public ContentModelManager() {
* @return the declared element which matches the given XML element and null
* otherwise.
*/
public CMElementDeclaration findCMElement(Node element) throws Exception {
public CMElementDeclaration findCMElement(Element element, String namespaceURI) throws Exception {
CMDocument cmDocument = findCMDocument(element, namespaceURI);
return cmDocument != null ? cmDocument.findCMElement(element, namespaceURI) : null;
}

public CMDocument findCMDocument(Element element, String namespaceURI) {
String systemId = null;
XMLDocument xmlDocument = element.getOwnerDocument();
SchemaLocation schemaLocation = xmlDocument.getSchemaLocation();
if (schemaLocation != null) {
String namespaceURI = xmlDocument.getNamespaceURI();
systemId = schemaLocation.getLocationHint(namespaceURI);
} else {
// TODO: implement CMDocument with DTD
}
if (systemId == null) {
systemId = findSystemIdFromFileAssociations(xmlDocument.getUri());
}
if (systemId == null) {
return null;
NoNamespaceSchemaLocation noNamespaceSchemaLocation = xmlDocument.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
systemId = noNamespaceSchemaLocation.getLocation();
} else {
// TODO : implement with DTD
}
}
CMDocument cmDocument = getCMDocument(null, systemId);
return cmDocument != null ? cmDocument.findCMElement(element) : null;
CMDocument cmDocument = findCMDocument(xmlDocument.getUri(), namespaceURI, systemId);
return cmDocument;
}

/**
Expand All @@ -93,17 +100,21 @@ public CMElementDeclaration findCMElement(Node element) throws Exception {
* @return the content model document loaded by the given uri and null
* otherwise.
*/
private CMDocument getCMDocument(String publicId, String systemId) {
private CMDocument findCMDocument(String uri, String publicId, String systemId) {
String key = publicId + systemId;
CMDocument cmDocument = cmDocumentCache.get(key);
if (cmDocument == null) {
CatalogResolver resolver = getCatalogResolver();
String uri = resolver != null ? resolver.getResolvedEntity(publicId, systemId) : systemId;
XSModel model = loader.loadURI(uri);
if (model != null) {
// XML Schema can be loaded
cmDocument = new XSDDocument(model);
cmDocumentCache.put(key, cmDocument);
String xmlSchemaURI = URIResolverExtensionManager.getInstance().resolve(uri, publicId, systemId);
if (xmlSchemaURI == null) {
xmlSchemaURI = systemId;
}
if (xmlSchemaURI != null) {
XSModel model = loader.loadURI(xmlSchemaURI);
if (model != null) {
// XML Schema can be loaded
cmDocument = new XSDDocument(model);
cmDocumentCache.put(key, cmDocument);
}
}
}
return cmDocument;
Expand All @@ -116,16 +127,13 @@ private CMDocument getCMDocument(String publicId, String systemId) {
*/
public void setCatalogs(String[] catalogs) {
if (catalogs != null) {
String xmlCatalogFiles = Stream.of(catalogs).filter(ContentModelManager::isXMLCatalogFileValid)
.map(Object::toString).collect(Collectors.joining(";"));
if (!xmlCatalogFiles.isEmpty()) {
CatalogManager catalogManager = new CatalogManager();
catalogManager.setUseStaticCatalog(false);
catalogManager.setIgnoreMissingProperties(true);
catalogManager.setCatalogFiles(xmlCatalogFiles);
catalogResolver = new CatalogResolver(catalogManager);
String[] xmlCatalogFiles = Stream.of(catalogs).filter(ContentModelManager::isXMLCatalogFileValid)
.collect(Collectors.toList()).toArray(new String[0]);
if (xmlCatalogFiles.length > 0) {
XMLCatalogResolver catalogResolver = new XMLCatalogResolver(xmlCatalogFiles);
catalogResolverExtension.setCatalogResolver(catalogResolver);
} else {
catalogResolver = null;
catalogResolverExtension.setCatalogResolver(null);
}
}

Expand All @@ -142,63 +150,13 @@ public static boolean isXMLCatalogFileValid(String catalogFile) {
return (file.exists());
}

/**
* Returns the catalog resolver to use and null otherwise.
*
* @return the catalog resolver to use and null otherwise.
*/
public CatalogResolver getCatalogResolver() {
return catalogResolver;
}

/**
* Set file associations.
*
* @param fileAssociations
*/
public void setFileAssociations(XMLFileAssociation[] fileAssociations) {
this.fileAssociations = fileAssociations;
}

/**
* Returns the XML Schema from the given file XML uri declared in an association
* and null otherwise.
*
* @param uri the file URI
* @return the XML Schema from the given file XML uri declared in an association
* and null otherwise.
* @throws SAXException
*/
public Schema findSchemaFromFileAssociations(String uri) throws SAXException {
String systemId = findSystemIdFromFileAssociations(uri);
if (systemId == null) {
return null;
}
File xmlSchemaFile = new File((systemId));
if (!xmlSchemaFile.exists()) {
return null;
}
return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(xmlSchemaFile);
}

/**
* Returns the system id (ex: XML Schema file path) from the given file XML uri
* declared in an association and null otherwise.
*
* @param uri the file URI.
* @return the system id (ex: XML Schema file path) from the given file XML uri
* and null otherwise.
*/
private String findSystemIdFromFileAssociations(String uri) {
if (fileAssociations == null) {
return null;
}
for (XMLFileAssociation fileAssociation : fileAssociations) {
if (fileAssociation.matches(uri)) {
return fileAssociation.getSystemId();
}
}
return null;
this.fileAssociationResolver.setFileAssociations(fileAssociations);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4xml.commons.BadLocationException;
import org.eclipse.lsp4xml.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lsp4xml.contentmodel.model.CMDocument;
import org.eclipse.lsp4xml.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.contentmodel.utils.XMLGenerator;
import org.eclipse.lsp4xml.model.Node;
import org.eclipse.lsp4xml.model.Element;
import org.eclipse.lsp4xml.model.XMLDocument;
import org.eclipse.lsp4xml.services.extensions.CompletionParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.ICompletionRequest;
Expand All @@ -35,40 +37,56 @@ public class ContentModelCompletionParticipant extends CompletionParticipantAdap

@Override
public void onTagOpen(ICompletionRequest request, ICompletionResponse response) throws Exception {
Node element = request.getParentNode();
Element element = (Element) request.getParentNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(element);
if (cmElement != null) {
XMLDocument document = element.getOwnerDocument();
int lineNumber = request.getPosition().getLine();
String lineText = document.lineText(lineNumber);
String lineDelimiter = document.lineDelimiter(lineNumber);
String whitespacesIndent = getStartWhitespaces(lineText);

XMLGenerator generator = new XMLGenerator(request.getFormattingSettings(), whitespacesIndent, lineDelimiter,
request.getCompletionSettings().isCompletionSnippetsSupported(), 0);
for (CMElementDeclaration child : cmElement.getElements()) {
String label = child.getName();
CompletionItem item = new CompletionItem(label);
item.setFilterText(label);
item.setKind(CompletionItemKind.Property);
String documentation = child.getDocumentation();
if (documentation != null) {
item.setDetail(documentation);
fillWithChildrenElementDeclaration(element, cmElement.getElements(), null, request, response);
}
if (element.equals(element.getOwnerDocument().getDocumentElement())) {
// root document element
Collection<String> prefixes = element.getAllPrefixes();
for (String prefix : prefixes) {
String namespaceURI = element.getNamespaceURI(prefix);
CMDocument cmDocument = ContentModelManager.getInstance().findCMDocument(element, namespaceURI);
if (cmDocument != null) {
fillWithChildrenElementDeclaration(element, cmDocument.getElements(), prefix, request, response);
}
String xml = generator.generate(child);
// Remove the first '<' character
xml = xml.substring(1, xml.length());
item.setTextEdit(new TextEdit(request.getReplaceRange(), xml));
item.setInsertTextFormat(InsertTextFormat.Snippet);
response.addCompletionItem(item);
}
}
}

private void fillWithChildrenElementDeclaration(Element element, Collection<CMElementDeclaration> cmElements,
String prefix, ICompletionRequest request, ICompletionResponse response) throws BadLocationException {
XMLDocument document = element.getOwnerDocument();
int lineNumber = request.getPosition().getLine();
String lineText = document.lineText(lineNumber);
String lineDelimiter = document.lineDelimiter(lineNumber);
String whitespacesIndent = getStartWhitespaces(lineText);

XMLGenerator generator = new XMLGenerator(request.getFormattingSettings(), whitespacesIndent, lineDelimiter,
request.getCompletionSettings().isCompletionSnippetsSupported(), 0);
for (CMElementDeclaration child : cmElements) {
String label = child.getName(prefix);
CompletionItem item = new CompletionItem(label);
item.setFilterText(label);
item.setKind(CompletionItemKind.Property);
String documentation = child.getDocumentation();
if (documentation != null) {
item.setDetail(documentation);
}
String xml = generator.generate(child, prefix);
// Remove the first '<' character
xml = xml.substring(1, xml.length());
item.setTextEdit(new TextEdit(request.getReplaceRange(), xml));
item.setInsertTextFormat(InsertTextFormat.Snippet);
response.addCompletionItem(item);
}
}

@Override
public void onAttributeName(String value, Range fullRange, ICompletionRequest completionRequest,
ICompletionResponse completionResponse) throws Exception {
Node element = completionRequest.getParentNode();
Element element = (Element) completionRequest.getParentNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(element);
if (cmElement != null) {
Collection<CMAttributeDeclaration> attributes = cmElement.getAttributes();
Expand All @@ -95,7 +113,7 @@ public void onAttributeName(String value, Range fullRange, ICompletionRequest co
@Override
public void onAttributeValue(String valuePrefix, Range fullRange, boolean addQuotes,
ICompletionRequest completionRequest, ICompletionResponse completionResponse) throws Exception {
Node element = completionRequest.getParentNode();
Element element = (Element) completionRequest.getParentNode();
CMElementDeclaration cmElement = ContentModelManager.getInstance().findCMElement(element);
if (cmElement != null) {
String attributeName = completionRequest.getCurrentAttributeName();
Expand Down
Loading

0 comments on commit 2ccd125

Please sign in to comment.