Skip to content

Commit

Permalink
Added document links for xsi:schemaLocation
Browse files Browse the repository at this point in the history
Every second token for the value of xsi:schemaLocation is turned into a
document link to the referenced schema file.

Closes eclipse-lemminx#666

Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 committed Jun 11, 2020
1 parent 3b5242e commit 040331c
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> schemaLocationValuePairs;
private final Map<String, SchemaLocationHint> 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 <code>SchemaLocationHint</code>
*/
public Collection<SchemaLocationHint> getSchemaLocationHints() {
return schemaLocationValuePairs.values();
}

}
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 :
*
*
* <ul>
* <li>XML Schema xsi:noNamespaceSchemaLocation</li>
* <li>DTD SYSTEM (ex : <!DOCTYPE root-element SYSTEM "./extended.dtd" )</li>
* <li>XML Schema xsi:schemaLocation</li>
* </ul>
*
*
* @author Angelo ZERR
*
*/
Expand Down Expand Up @@ -88,11 +92,27 @@ public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
}
}
}
// Doc link for xsi:schemaLocation
SchemaLocation schemaLocation = document.getSchemaLocation();
if (schemaLocation != null) {
try {
Collection<SchemaLocationHint> 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) {
Expand Down

0 comments on commit 040331c

Please sign in to comment.