Skip to content

Commit

Permalink
Added document link for included schemas in .xsd
Browse files Browse the repository at this point in the history
The URI specified for the `schemaLocation` attribute of `xs:include` is now
clickable.
Fixes #689.

Signed-off-by: David Thompson <davthomp@redhat.com>
  • Loading branch information
datho7561 authored and angelozerr committed May 28, 2020
1 parent 47ba73d commit d2aefbd
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
import org.eclipse.lemminx.extensions.xsd.participants.XSDCodeLensParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.XSDCompletionParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.XSDDefinitionParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.XSDDocumentLinkParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.XSDHighlightingParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.XSDReferenceParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.XSDRenameParticipant;
import org.eclipse.lemminx.extensions.xsd.participants.diagnostics.XSDDiagnosticsParticipant;
import org.eclipse.lemminx.services.extensions.ICompletionParticipant;
import org.eclipse.lemminx.services.extensions.IDefinitionParticipant;
import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lemminx.services.extensions.IHighlightingParticipant;
import org.eclipse.lemminx.services.extensions.IReferenceParticipant;
import org.eclipse.lemminx.services.extensions.IRenameParticipant;
Expand All @@ -51,6 +53,7 @@ public class XSDPlugin implements IXMLExtension {
private final ICodeLensParticipant codeLensParticipant;
private final IHighlightingParticipant highlightingParticipant;
private final IRenameParticipant renameParticipant;
private final IDocumentLinkParticipant documentLinkParticipant;
private XSDURIResolverExtension uiResolver;

private ContentModelManager modelManager;
Expand All @@ -63,6 +66,7 @@ public XSDPlugin() {
codeLensParticipant = new XSDCodeLensParticipant();
highlightingParticipant = new XSDHighlightingParticipant();
renameParticipant = new XSDRenameParticipant();
documentLinkParticipant = new XSDDocumentLinkParticipant();
}

@Override
Expand Down Expand Up @@ -94,6 +98,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerCodeLensParticipant(codeLensParticipant);
registry.registerHighlightingParticipant(highlightingParticipant);
registry.registerRenameParticipant(renameParticipant);
registry.registerDocumentLinkParticipant(documentLinkParticipant);
}

@Override
Expand All @@ -106,5 +111,6 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterCodeLensParticipant(codeLensParticipant);
registry.unregisterHighlightingParticipant(highlightingParticipant);
registry.unregisterRenameParticipant(renameParticipant);
registry.unregisterDocumentLinkParticipant(documentLinkParticipant);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright (c) 2020 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
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*/
package org.eclipse.lemminx.extensions.xsd.participants;

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

import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.util.URI.MalformedURIException;
import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.extensions.xsd.utils.XSDUtils;
import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.DocumentLink;
import org.w3c.dom.Element;

/**
*
* Implements document links in .xsd files for
* <ul>
* <li>xs:include schemaLocation</li>
* </ul>
*
*/
public class XSDDocumentLinkParticipant implements IDocumentLinkParticipant {

private static final Logger LOGGER = Logger.getLogger(XSDDocumentLinkParticipant.class.getName());

@Override
public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
DOMElement root = document.getDocumentElement();
if (root == null || !XSDUtils.isXSSchema(root)) {
return;
}
String xmlSchemaPrefix = root.getPrefix();
List<DOMNode> children = root.getChildren();
for (DOMNode child : children) {
if (child.isElement() && XSDUtils.isXSInclude((Element) child)
&& Objects.equals(child.getPrefix(), xmlSchemaPrefix)) {
DOMElement includeElement = (DOMElement) child;
DOMAttr schemaLocationAttr = XSDUtils.getSchemaLocation(includeElement);
if (schemaLocationAttr != null && !StringUtils.isEmpty(schemaLocationAttr.getValue())) {
String location = getResolvedLocation(document.getDocumentURI(), schemaLocationAttr.getValue());
DOMRange schemaLocationRange = schemaLocationAttr.getNodeAttrValue();
try {
links.add(createDocumentLink(schemaLocationRange, location));
} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE, "Creation of document link failed", e);
}
}
}
}
}

private static String getResolvedLocation(String documentURI, String location) {
if (location == null) {
return null;
}
try {
return XMLEntityManager.expandSystemId(location, documentURI, false);
} catch (MalformedURIException e) {
return location;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.util.Vector;
import java.util.function.BiConsumer;

import com.google.common.base.Objects;

import org.apache.xerces.impl.xs.SchemaGrammar;
import org.apache.xerces.xs.StringList;
import org.eclipse.lemminx.dom.DOMAttr;
Expand All @@ -37,8 +39,6 @@
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.common.base.Objects;

/**
* XSD utilities.
*
Expand Down Expand Up @@ -404,6 +404,10 @@ public static boolean isXSAttribute(DOMElement element) {
return "attribute".equals(element.getLocalName());
}

public static boolean isXSSchema(Element element) {
return "schema".equals(element.getLocalName());
}

public static FilesChangedTracker createFilesChangedTracker(SchemaGrammar grammar) {
return createFilesChangedTracker(Collections.singleton(grammar));
}
Expand Down Expand Up @@ -444,4 +448,11 @@ private static void updateTracker(SchemaGrammar grammar, Set<SchemaGrammar> trac
}
}
}

public static DOMAttr getSchemaLocation(DOMElement includeElement) {
if (!isXSInclude(includeElement)) {
return null;
}
return includeElement.getAttributeNode("schemaLocation");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright (c) 2020 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
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*/
package org.eclipse.lemminx.extensions.xsd;

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;

/**
* Tests for the docuement links in .xsd provided by <code>XSDDocumentLinkParticipant</code>
*
* @see org.eclipse.lemminx.extensions.xsd.participants.XSDDocumentLinkParticipant
*/
public class XSDDocumentLinkingExtensionsTest {

@Test
public void xsIncludeUsualNamespace() throws BadLocationException {
String xml = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n" + //
" <xs:include schemaLocation=\"choice.xsd\"></xs:include>\n" + //
" <xs:element name=\"int\">\n" + //
" <xs:simpleType>\n" + //
" <xs:restriction base=\"xs:integer\"/>\n" + //
" </xs:simpleType>\n" + //
" </xs:element>\n" + //
"</xs:schema>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xsd/unnamed-integer.xsd",
dl(r(1, 32, 1, 42), "src/test/resources/xsd/choice.xsd"));
}

@Test
public void xsIncludeDifferentNamespace() throws BadLocationException {
String xml = "<schemanamespace:schema xmlns:schemanamespace=\"http://www.w3.org/2001/XMLSchema\">\n" + //
" <schemanamespace:include schemaLocation=\"choice.xsd\"></schemanamespace:include>\n" + //
" <schemanamespace:element name=\"int\">\n" + //
" <schemanamespace:simpleType>\n" + //
" <schemanamespace:restriction base=\"schemanamespace:integer\"/>\n" + //
" </schemanamespace:simpleType>\n" + //
" </schemanamespace:element>\n" + //
"</schemanamespace:schema>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xsd/unnamed-integer.xsd",
dl(r(1, 45, 1, 55), "src/test/resources/xsd/choice.xsd"));
}

@Test
public void xsIncludeEmptySchemaLocation() throws BadLocationException {
String xml = "<xs:schema xmlns:xs=\"http://example.org\">\n" + //
" <xs:include schemaLocation=\"\"></xs:include>\n" + //
" <xs:element name=\"int\">\n" + //
" <xs:simpleType>\n" + //
" <xs:restriction base=\"xs:integer\"/>\n" + //
" </xs:simpleType>\n" + //
" </xs:element>\n" + //
"</xs:schema>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xsd/unnamed-integer.xsd");
}

@Test
public void xsIncludeManyOccurences() throws BadLocationException {
String xml = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n" + //
" <xs:include schemaLocation=\"choice.xsd\"></xs:include>\n" + //
" <xs:include schemaLocation=\"pattern.xsd\"></xs:include>\n" + //
" <xs:element name=\"int\">\n" + //
" <xs:simpleType>\n" + //
" <xs:restriction base=\"xs:integer\"/>\n" + //
" </xs:simpleType>\n" + //
" </xs:element>\n" + //
"</xs:schema>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xsd/unnamed-integer.xsd",
dl(r(1, 32, 1, 42), "src/test/resources/xsd/choice.xsd"),
dl(r(2, 32, 2, 43), "src/test/resources/xsd/pattern.xsd"));
}

@Test
public void xsIncludeNoSchemaLocation() throws BadLocationException {
String xml = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n" + //
" <xs:include />\n" + //
" <xs:element name=\"int\">\n" + //
" <xs:simpleType>\n" + //
" <xs:restriction base=\"xs:integer\"/>\n" + //
" </xs:simpleType>\n" + //
" </xs:element>\n" + //
"</xs:schema>";
XMLAssert.testDocumentLinkFor(xml, "src/test/resources/xsd/unnamed-integer.xsd");
}

}

0 comments on commit d2aefbd

Please sign in to comment.