Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for document link DTD entity SYSTEM #1166

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"));
}
}