diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DTDEntityDecl.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DTDEntityDecl.java index 6541cc44b..749c10d7e 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DTDEntityDecl.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DTDEntityDecl.java @@ -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); } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelDocumentLinkParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelDocumentLinkParticipant.java index 0ce9f069c..444a4522a 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelDocumentLinkParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelDocumentLinkParticipant.java @@ -21,6 +21,7 @@ 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; @@ -28,6 +29,7 @@ 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 : @@ -35,6 +37,8 @@ * * @@ -51,25 +55,10 @@ public ContentModelDocumentLinkParticipant(URIResolverExtensionManager resolverM @Override public void findDocumentLinks(DOMDocument document, List 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) { + // links) { // Do nothing } } + + // + 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 xmlModels = document.getXMLModels(); for (XMLModel xmlModel : xmlModels) { @@ -98,6 +106,24 @@ public void findDocumentLinks(DOMDocument document, List 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) { diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/DTDPlugin.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/DTDPlugin.java index a70c1a26f..2537c0a27 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/DTDPlugin.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/DTDPlugin.java @@ -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; @@ -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; /** @@ -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(); @@ -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 @@ -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); } -} +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/participants/DTDDocumentLinkParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/participants/DTDDocumentLinkParticipant.java new file mode 100644 index 000000000..cf36ba643 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/participants/DTDDocumentLinkParticipant.java @@ -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: + * + * + * + * + * + * @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 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 + // + DOMRange systemIdRange = entity.getSystemIdNode(); + if (systemIdRange != null) { + links.add(createDocumentLink(systemIdRange, location, true)); + } + } catch (BadLocationException e) { + // Do nothing + } + } + } + } + } + +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/DTDDocumentLinkTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/DTDDocumentLinkTest.java index 171d5655d..8f3a8e9f3 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/DTDDocumentLinkTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/DTDDocumentLinkTest.java @@ -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 = "\r\n" + // + " %document;\r\n" + // + " ]> \r\n" + // + " \r\n" + // + " "; + 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 @@ -70,6 +83,6 @@ public void linkWithCatalogAndPublic() throws Exception { ""; 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")); } } diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/dtd/DTDDocumentLinkTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/dtd/DTDDocumentLinkTest.java new file mode 100644 index 000000000..f8db29224 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/dtd/DTDDocumentLinkTest.java @@ -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 = ""; + XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xml/base.dtd", + dl(r(0, 28, 0, 43), "src/test/resources/document.ent")); + } +}