Skip to content

Commit

Permalink
XML completion based on DTD with XML catalog and PUBLIC declaration
Browse files Browse the repository at this point in the history
doesn't work

Fixes #849

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
azerr authored and angelozerr committed Aug 20, 2020
1 parent ebb62a7 commit f3ab019
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 22 deletions.
Expand Up @@ -23,6 +23,7 @@
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelProvider.Identifier;
import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.LSPXMLGrammarPool;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLFileAssociation;
import org.eclipse.lemminx.extensions.contentmodel.uriresolver.XMLCacheResolverExtension;
Expand Down Expand Up @@ -116,10 +117,12 @@ public Collection<CMDocument> findCMDocument(DOMDocument xmlDocument, String nam
// The content model provider can collect the system ids
// ex for <?xml-model , the model provider which takes care of xml-model returns
// the href of xml-model.
Collection<String> systemIds = modelProvider.getSystemIds(xmlDocument, namespaceURI);
for (String systemId : systemIds) {
Collection<Identifier> identifiers = modelProvider.getIdentifiers(xmlDocument, namespaceURI);
for (Identifier identifier : identifiers) {
String systemId = identifier.getSystemId();
String publicId = identifier.getPublicId() != null ? identifier.getPublicId() : namespaceURI;
// get the content model document from the current system id
CMDocument cmDocument = findCMDocument(xmlDocument.getDocumentURI(), namespaceURI, systemId,
CMDocument cmDocument = findCMDocument(xmlDocument.getDocumentURI(), publicId, systemId,
modelProvider);
if (cmDocument != null) {
documents.add(cmDocument);
Expand Down Expand Up @@ -151,9 +154,11 @@ public boolean dependsOnGrammar(DOMDocument document, String grammarURI) {
}
for (ContentModelProvider modelProvider : modelProviders) {
if (modelProvider.adaptFor(document, false)) {
Collection<String> systemIds = modelProvider.getSystemIds(document, document.getNamespaceURI());
for (String systemId : systemIds) {
String key = resolverManager.resolve(document.getDocumentURI(), null, systemId);
Collection<Identifier> identifiers = modelProvider.getIdentifiers(document, document.getNamespaceURI());
for (Identifier identifier : identifiers) {
String publicId = identifier.getPublicId();
String systemId = identifier.getSystemId();
String key = resolverManager.resolve(document.getDocumentURI(), publicId, systemId);
if (grammarURI.equals(key)) {
return true;
}
Expand Down
Expand Up @@ -22,13 +22,34 @@
*/
public interface ContentModelProvider {

public class Identifier {

private final String publicId;

private final String systemId;

public Identifier(String publicId, String systemId) {
this.publicId = publicId;
this.systemId = systemId;
}

public String getPublicId() {
return publicId;
}

public String getSystemId() {
return systemId;
}

}

/**
* Returns the content model provider by using standard association
* (xsi:schemaLocation, xsi:noNamespaceSchemaLocation, doctype) an dnull
* otherwise.
*
* @param document
* @param internal
* @param internal
* @return the content model provider by using standard association
* (xsi:schemaLocation, xsi:noNamespaceSchemaLocation, doctype) an dnull
* otherwise.
Expand All @@ -37,7 +58,7 @@ public interface ContentModelProvider {

boolean adaptFor(String uri);

Collection<String> getSystemIds(DOMDocument xmlDocument, String namespaceURI);
Collection<Identifier> getIdentifiers(DOMDocument xmlDocument, String namespaceURI);

CMDocument createCMDocument(String key);

Expand Down
Expand Up @@ -72,14 +72,15 @@ public boolean adaptFor(String uri) {
}

@Override
public Collection<String> getSystemIds(DOMDocument xmlDocument, String namespaceURI) {
public Collection<Identifier> getIdentifiers(DOMDocument xmlDocument, String namespaceURI) {
/*
* <!DOCTYPE catalog PUBLIC
* "-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN"
* "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
*/
DOMDocumentType documentType = xmlDocument.getDoctype();
return Collections.singleton(documentType.getSystemIdWithoutQuotes());
return Collections.singleton(
new Identifier(documentType.getPublicIdWithoutQuotes(), documentType.getSystemIdWithoutQuotes()));
}

@Override
Expand Down
Expand Up @@ -47,9 +47,12 @@ public boolean adaptFor(String uri) {
}

@Override
public Collection<String> getSystemIds(DOMDocument xmlDocument, String namespaceURI) {
return xmlDocument.getXMLModels().stream().map(node -> node.getHref())
.filter(href -> !StringUtils.isEmpty(href)).collect(Collectors.toList());
public Collection<Identifier> getIdentifiers(DOMDocument xmlDocument, String namespaceURI) {
return xmlDocument.getXMLModels().stream() //
.map(node -> node.getHref()) //
.filter(href -> !StringUtils.isEmpty(href)) //
.map(href -> new Identifier(null, href)) //
.collect(Collectors.toList());
}

@Override
Expand Down
Expand Up @@ -55,13 +55,13 @@ public boolean adaptFor(String uri) {
}

@Override
public Collection<String> getSystemIds(DOMDocument xmlDocument, String namespaceURI) {
Collection<String> systemIds = new ArrayList<>();
public Collection<Identifier> getIdentifiers(DOMDocument xmlDocument, String namespaceURI) {
Collection<Identifier> identifiers = new ArrayList<>();
SchemaLocation schemaLocation = xmlDocument.getSchemaLocation();
if (schemaLocation != null) {
String location = schemaLocation.getLocationHint(namespaceURI);
if (!StringUtils.isEmpty(location)) {
systemIds.add(location);
identifiers.add(new Identifier(null, location));
}
} else {
NoNamespaceSchemaLocation noNamespaceSchemaLocation = xmlDocument.getNoNamespaceSchemaLocation();
Expand All @@ -70,12 +70,12 @@ public Collection<String> getSystemIds(DOMDocument xmlDocument, String namespace
// xsi:noNamespaceSchemaLocation doesn't define namespaces
String location = noNamespaceSchemaLocation.getLocation();
if (!StringUtils.isEmpty(location)) {
systemIds.add(location);
identifiers.add(new Identifier(null, location));
}
}
}
}
return systemIds;
return identifiers;
}

@Override
Expand All @@ -94,7 +94,7 @@ public CMDocument createInternalCMDocument(DOMDocument xmlDocument) {
return null;
}

public XSLoaderImpl getLoader() {
public XSLoaderImpl getLoader() {
XSLoaderImpl loader = new XSLoaderImpl();
loader.setParameter("http://apache.org/xml/properties/internal/entity-resolver", resolverExtensionManager);
loader.setParameter(Constants.DOM_ERROR_HANDLER, new DOMErrorHandler() {
Expand Down
Expand Up @@ -11,9 +11,9 @@
*******************************************************************************/
package org.eclipse.lemminx.extensions.contentmodel;

import static org.eclipse.lemminx.XMLAssert.CDATA_SNIPPETS;
import static org.eclipse.lemminx.XMLAssert.COMMENT_SNIPPETS;
import static org.eclipse.lemminx.XMLAssert.DTDNODE_SNIPPETS;
import static org.eclipse.lemminx.XMLAssert.CDATA_SNIPPETS;
import static org.eclipse.lemminx.XMLAssert.PROCESSING_INSTRUCTION_SNIPPETS;
import static org.eclipse.lemminx.XMLAssert.c;
import static org.eclipse.lemminx.XMLAssert.te;
Expand Down Expand Up @@ -241,20 +241,42 @@ public void attributeCompletionWithDoctypeSubsetWithNoElements() throws BadLocat
"xmlns:xsi"));
}

@Test
public void completionWithCatalogAndPublic() throws Exception {
// This test uses the local DTD with catalog-public.xml by using the PUBLIC ID
// -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN
// <public publicId="-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
// uri="../dtd/web-app_2_3.dtd" />
String xml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> \r\n" + //
"<!DOCTYPE web-app\r\n" + //
" PUBLIC \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\r\n" + //
" \"ABCD.dtd\">\r\n" + //
"\r\n" + //
"<web-app>|</web-app>";
testCompletionFor(xml, true, null, "catalog-public.xml",
c("icon", te(5, 9, 5, 9, "<icon>$1</icon>$0"), "icon"), //
c("display-name", te(5, 9, 5, 9, "<display-name>$1</display-name>$0"), "display-name"));
}

private void testCompletionFor(String xml, CompletionItem... expectedItems) throws BadLocationException {
testCompletionFor(xml, true, null, expectedItems);
}

private void testCompletionFor(String xml, boolean isSnippetsSupported, Integer expectedCount,
CompletionItem... expectedItems) throws BadLocationException {
testCompletionFor(xml, isSnippetsSupported, expectedCount, "catalog.xml", expectedItems);
}

private void testCompletionFor(String xml, boolean isSnippetsSupported, Integer expectedCount, String catalog,
CompletionItem... expectedItems) throws BadLocationException {
CompletionCapabilities completionCapabilities = new CompletionCapabilities();
CompletionItemCapabilities completionItem = new CompletionItemCapabilities(isSnippetsSupported); // activate
// snippets
completionCapabilities.setCompletionItem(completionItem);

SharedSettings sharedSettings = new SharedSettings();
sharedSettings.getCompletionSettings().setCapabilities(completionCapabilities);
XMLAssert.testCompletionFor(new XMLLanguageService(), xml, "src/test/resources/catalogs/catalog.xml", null,
null, expectedCount, sharedSettings, expectedItems);
XMLAssert.testCompletionFor(new XMLLanguageService(), xml, "src/test/resources/catalogs/" + catalog, null, null,
expectedCount, sharedSettings, expectedItems);
}
}

0 comments on commit f3ab019

Please sign in to comment.