From 040331c6aa8d8839e2a9a25be1df656087ff14ec Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 9 Jun 2020 15:53:57 -0400 Subject: [PATCH] Added document links for xsi:schemaLocation Every second token for the value of xsi:schemaLocation is turned into a document link to the referenced schema file. Closes #666 Signed-off-by: David Thompson --- .../eclipse/lemminx/dom/SchemaLocation.java | 42 ++++++++--- .../lemminx/dom/SchemaLocationHint.java | 70 +++++++++++++++++++ .../ContentModelDocumentLinkParticipant.java | 26 ++++++- 3 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocationHint.java diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocation.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocation.java index 98939a8546..4cb744e847 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocation.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocation.java @@ -12,40 +12,62 @@ */ package org.eclipse.lemminx.dom; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** - * + * * The declared "xsi:schemaLocation" */ public class SchemaLocation { - private final Map schemaLocationValuePairs; + private final Map schemaLocationValuePairs; private final DOMAttr attr; + // The text to match is of the form: + // http://example.org root.xsd http://example.org bison.xsd http://example.org potato.xsd + private static final Pattern SCHEMA_LOCATION_PAIR_PATTERN = Pattern.compile("\\s*([^\\s]+)\\s+([^\\s]+)\\s*"); + public SchemaLocation(String base, DOMAttr attr) { this.attr = attr; this.schemaLocationValuePairs = new HashMap<>(); String value = attr.getValue(); - StringTokenizer st = new StringTokenizer(value); - do { - String namespaceURI = st.hasMoreTokens() ? st.nextToken() : null; - String locationHint = st.hasMoreTokens() ? st.nextToken() : null; + Matcher locPairMatcher = SCHEMA_LOCATION_PAIR_PATTERN.matcher(value); + while (locPairMatcher.find()) { + String namespaceURI = locPairMatcher.group(1); + String locationHint = locPairMatcher.group(2); if (namespaceURI == null || locationHint == null) break; - schemaLocationValuePairs.put(namespaceURI, locationHint); - } while (true); + DOMNode valNode = attr.getNodeAttrValue(); + // matcher matches 1 char ahead, but valNode's start is at beginning of " + int start = valNode.getStart() + locPairMatcher.start(2); + // matcher matches end correctly, but range needs to be one past end + // to highlight the end, and one to make up for " + int end = valNode.getStart() + locPairMatcher.end(2) + 2; + schemaLocationValuePairs.put(namespaceURI, + new SchemaLocationHint(start, end, locationHint, this)); + } } public String getLocationHint(String namespaceURI) { - return schemaLocationValuePairs.get(namespaceURI); + return schemaLocationValuePairs.get(namespaceURI).getHint(); } public DOMAttr getAttr() { return attr; } + /** + * Gets all the location hints given in this xsi:schemaLocation attribute + * + * @return A Collection of all the location hints as SchemaLocationHint + */ + public Collection getSchemaLocationHints() { + return schemaLocationValuePairs.values(); + } + } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocationHint.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocationHint.java new file mode 100644 index 0000000000..7b099320b4 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/SchemaLocationHint.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2018 Red Hat Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * 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.dom; + +/** + * Represents one of the location hints provided in an xsi:schemaLocation attribute. + */ +public class SchemaLocationHint implements DOMRange { + + private final int start, end; + + private final String hint; + + private final SchemaLocation parent; + + /** + * + * @param start The offset from the beginning of the document where this location hint starts + * @param end The offset from the beginning of the document where this location hint ends + * @param hint The hint to the location of a schema (A URI that points to a schema) + * @param parent The SchemaLocation in which this hint was given + */ + public SchemaLocationHint(int start, int end, String hint, SchemaLocation parent) { + this.start = start; + this.end = end; + this.hint = hint; + this.parent = parent; + } + + /** + * Get the location hint that this SchemaLocationHint represents + * + * @return The location hint, a URI to a schema, as a String + */ + public String getHint() { + return this.hint; + } + + /** + * @return The offset from the beginning of the document where this location hint starts + */ + @Override + public int getStart() { + return this.start; + } + + /** + * @return The offset from the beginning of the document where this location hint ends + */ + @Override + public int getEnd() { + return this.end; + } + + @Override + public DOMDocument getOwnerDocument() { + return parent.getAttr().getOwnerDocument(); + } + +} \ No newline at end of file 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 ce3392d9d2..5a4a924eb4 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 @@ -14,6 +14,7 @@ import static org.eclipse.lemminx.utils.XMLPositionUtility.createDocumentLink; +import java.util.Collection; import java.util.List; import org.apache.xerces.impl.XMLEntityManager; @@ -23,18 +24,21 @@ import org.eclipse.lemminx.dom.DOMDocumentType; import org.eclipse.lemminx.dom.DOMRange; 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.lsp4j.DocumentLink; /** * Document link for : - * + * *
    *
  • XML Schema xsi:noNamespaceSchemaLocation
  • *
  • DTD SYSTEM (ex : + *
  • XML Schema xsi:schemaLocation
  • *
- * + * * @author Angelo ZERR * */ @@ -88,11 +92,27 @@ public void findDocumentLinks(DOMDocument document, List links) { } } } + // Doc link for xsi:schemaLocation + SchemaLocation schemaLocation = document.getSchemaLocation(); + if (schemaLocation != null) { + try { + Collection schemaLocationHints = schemaLocation.getSchemaLocationHints(); + String location; + for (SchemaLocationHint schemaLocationHint : schemaLocationHints) { + location = getResolvedLocation(document.getDocumentURI(), schemaLocationHint.getHint()); + if (location != null) { + links.add(createDocumentLink(schemaLocationHint, location)); + } + } + } catch (BadLocationException e) { + // Do nothing + } + } } /** * Returns the expanded system location - * + * * @return the expanded system location */ private static String getResolvedLocation(String documentURI, String location) {