Skip to content

Commit

Permalink
Support for document link DTD entity SYSTEM
Browse files Browse the repository at this point in the history
Fixes eclipse-lemminx#1165

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Feb 7, 2022
1 parent ba438fe commit 4d85bbd
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ public String getSystemId() {
return systemId != null ? systemId.getParameterWithoutFirstAndLastChar() : null;
}

/**
* Returns the system if node and null otherwise.
*
* @return the system if node and null otherwise.
*/
public DTDDeclParameter getSystemIdNode() {
return systemId;
}

public void setSystemId(int start, int end) {
systemId = addNewParameter(start, end);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMDocumentType;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.dom.DTDEntityDecl;
import org.eclipse.lemminx.dom.NoNamespaceSchemaLocation;
import org.eclipse.lemminx.dom.SchemaLocation;
import org.eclipse.lemminx.dom.SchemaLocationHint;
import org.eclipse.lemminx.dom.XMLModel;
import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4j.DocumentLink;
import org.w3c.dom.NamedNodeMap;

/**
* Document link for :
*
* <ul>
* <li>XML Schema xsi:noNamespaceSchemaLocation</li>
* <li>DTD SYSTEM (ex : <!DOCTYPE root-element SYSTEM "./extended.dtd" )</li>
* <li>Entity SYSTEM (ex : <!ENTITY % xinclude SYSTEM
* "http://www.docbook.org/xml/4.4/xinclude.mod"> )</li>
* <li>XML Schema xsi:schemaLocation</li>
* </ul>
*
Expand All @@ -51,25 +55,10 @@ public ContentModelDocumentLinkParticipant(URIResolverExtensionManager resolverM

@Override
public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
// Document link for xsi:noNamespaceSchemaLocation
NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
try {
String location = resolverManager.resolve(document.getDocumentURI(), null,
noNamespaceSchemaLocation.getLocation());
if (location != null) {
DOMRange attrValue = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
if (attrValue != null) {
links.add(createDocumentLink(attrValue, location, true));
}
}
} catch (BadLocationException e) {
// Do nothing
}
}
// Document link for DTD
DOMDocumentType docType = document.getDoctype();
if (docType != null) {
// <!DOCTYPE root-element SYSTEM "./extended.dtd"
String location = resolverManager.resolve(document.getDocumentURI(), docType.getPublicIdWithoutQuotes(),
docType.getSystemIdWithoutQuotes());
if (location != null) {
Expand All @@ -82,7 +71,26 @@ public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
// Do nothing
}
}

// <!ENTITY % xinclude SYSTEM "http://www.docbook.org/xml/4.4/xinclude.mod">
NamedNodeMap entities = docType.getEntities();
for (int i = 0; i < entities.getLength(); i++) {
DTDEntityDecl entity = (DTDEntityDecl) entities.item(i);
location = resolverManager.resolve(document.getDocumentURI(), entity.getPublicId(),
entity.getSystemId());
if (location != null) {
try {
DOMRange systemIdRange = entity.getSystemIdNode();
if (systemIdRange != null) {
links.add(createDocumentLink(systemIdRange, location, true));
}
} catch (BadLocationException e) {
// Do nothing
}
}
}
}

// Document link for xml-model/href
List<XMLModel> xmlModels = document.getXMLModels();
for (XMLModel xmlModel : xmlModels) {
Expand All @@ -98,6 +106,24 @@ public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
}
}
}

// Document link for xsi:noNamespaceSchemaLocation
NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
try {
String location = resolverManager.resolve(document.getDocumentURI(), null,
noNamespaceSchemaLocation.getLocation());
if (location != null) {
DOMRange attrValue = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
if (attrValue != null) {
links.add(createDocumentLink(attrValue, location, true));
}
}
} catch (BadLocationException e) {
// Do nothing
}
}

// Doc link for xsi:schemaLocation
SchemaLocation schemaLocation = document.getSchemaLocation();
if (schemaLocation != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.eclipse.lemminx.extensions.dtd.contentmodel.CMDTDContentModelProvider;
import org.eclipse.lemminx.extensions.dtd.participants.DTDCodeLensParticipant;
import org.eclipse.lemminx.extensions.dtd.participants.DTDDefinitionParticipant;
import org.eclipse.lemminx.extensions.dtd.participants.DTDDocumentLinkParticipant;
import org.eclipse.lemminx.extensions.dtd.participants.DTDHighlightingParticipant;
import org.eclipse.lemminx.extensions.dtd.participants.DTDReferenceParticipant;
import org.eclipse.lemminx.extensions.dtd.participants.diagnostics.DTDDiagnosticsParticipant;
Expand All @@ -27,6 +28,7 @@
import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lemminx.services.extensions.codelens.ICodeLensParticipant;
import org.eclipse.lemminx.services.extensions.diagnostics.IDiagnosticsParticipant;
import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4j.InitializeParams;

/**
Expand All @@ -39,6 +41,7 @@ public class DTDPlugin implements IXMLExtension {
private final IHighlightingParticipant highlightingParticipant;
private final IReferenceParticipant referenceParticipant;
private final ICodeLensParticipant codeLensParticipant;
private DTDDocumentLinkParticipant documentLinkParticipant;

public DTDPlugin() {
definitionParticipant = new DTDDefinitionParticipant();
Expand All @@ -64,6 +67,9 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerReferenceParticipant(referenceParticipant);
// register codelens participant
registry.registerCodeLensParticipant(codeLensParticipant);
// register document link participant
URIResolverExtensionManager resolverManager = registry.getComponent(URIResolverExtensionManager.class);
documentLinkParticipant = new DTDDocumentLinkParticipant(resolverManager);
}

@Override
Expand All @@ -78,5 +84,7 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterReferenceParticipant(referenceParticipant);
// unregister codelens participant
registry.unregisterCodeLensParticipant(codeLensParticipant);
// unregister document link participant
registry.unregisterDocumentLinkParticipant(documentLinkParticipant);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2022 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
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.dtd.participants;

import static org.eclipse.lemminx.utils.XMLPositionUtility.createDocumentLink;

import java.util.List;

import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMDocumentType;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.dom.DTDEntityDecl;
import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4j.DocumentLink;
import org.w3c.dom.NamedNodeMap;

/**
* Document link for DTD entities system inside a DTD:
*
* <code>
* <!ENTITY % xinclude SYSTEM "http://www.docbook.org/xml/4.4/xinclude.mod">
* </code>
*
* @author Angelo ZERR
*
*/
public class DTDDocumentLinkParticipant implements IDocumentLinkParticipant {

private final URIResolverExtensionManager resolverManager;

public DTDDocumentLinkParticipant(URIResolverExtensionManager resolverManager) {
this.resolverManager = resolverManager;
}

@Override
public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
// Document link for DTD
DOMDocumentType docType = document.getDoctype();
if (docType != null) {
// Loop for each entities declaration
NamedNodeMap entities = docType.getEntities();
for (int i = 0; i < entities.getLength(); i++) {
DTDEntityDecl entity = (DTDEntityDecl) entities.item(i);
String location = resolverManager.resolve(document.getDocumentURI(), entity.getPublicId(),
entity.getSystemId());
if (location != null) {
try {
// The entity declares a SYSTEM like
// <!ENTITY % xinclude SYSTEM "http://www.docbook.org/xml/4.4/xinclude.mod">
DOMRange systemIdRange = entity.getSystemIdNode();
if (systemIdRange != null) {
links.add(createDocumentLink(systemIdRange, location, true));
}
} catch (BadLocationException e) {
// Do nothing
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ public void noLinks() throws BadLocationException {
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xml/base.xml");
}

@Test
public void entity() throws BadLocationException {
String xml = "<!DOCTYPE chapter PUBLIC \"-//OASIS//DTD DocBook XML V4.4//EN\" \"../docbookx.dtd\" [\r\n" + //
" <!ENTITY % document SYSTEM \"../document.ent\">\r\n" + //
" %document;\r\n" + //
" ]> \r\n" + //
" <chapter>\r\n" + //
" </chapter>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xml/base.xml",
dl(r(0, 63, 0, 78), "src/test/resources/docbookx.dtd"), //
dl(r(1, 29, 1, 44), "src/test/resources/document.ent"));
}

@Test
public void linkWithCatalogAndPublic() throws Exception {
// This test uses the local DTD with catalog-public.xml by using the PUBLIC ID
Expand All @@ -70,6 +83,6 @@ public void linkWithCatalogAndPublic() throws Exception {
"<web-app></web-app>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xml/base.xml",
"src/test/resources/catalogs/catalog-public.xml",
dl(r(3,4,3,12), "src/test/resources/dtd/web-app_2_3.dtd"));
dl(r(3, 4, 3, 12), "src/test/resources/dtd/web-app_2_3.dtd"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2022 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
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.dtd;

import static org.eclipse.lemminx.XMLAssert.dl;
import static org.eclipse.lemminx.XMLAssert.r;

import org.eclipse.lemminx.XMLAssert;
import org.eclipse.lemminx.commons.BadLocationException;
import org.junit.jupiter.api.Test;

/**
* DTD entities document links tests inside a DTD.
*
*/
public class DTDDocumentLinkTest {

@Test
public void entity() throws BadLocationException {
String xml = "<!ENTITY % document SYSTEM \"../document.ent\">";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xml/base.dtd",
dl(r(0, 28, 0, 43), "src/test/resources/document.ent"));
}
}

0 comments on commit 4d85bbd

Please sign in to comment.