Skip to content

Commit

Permalink
Add support for textDocument/references for XML Schema types
Browse files Browse the repository at this point in the history
Fix #58

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jun 25, 2019
1 parent 9d8e118 commit fe77ac6
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ public CompletableFuture<Either<List<? extends Location>, List<? extends Locatio
TextDocumentPositionParams params) {
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
if (definitionLinkSupport) {
return Either.forRight(getXMLLanguageService().findDefinition(xmlDocument, params.getPosition(), cancelChecker));
return Either.forRight(
getXMLLanguageService().findDefinition(xmlDocument, params.getPosition(), cancelChecker));
}
List<? extends Location> locations = getXMLLanguageService()
.findDefinition(xmlDocument, params.getPosition(), cancelChecker) //
Expand All @@ -299,7 +300,8 @@ public CompletableFuture<Either<List<? extends Location>, List<? extends Locatio
@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
return getXMLLanguageService().findReferences(xmlDocument, params.getPosition(), params.getContext());
return getXMLLanguageService().findReferences(xmlDocument, params.getPosition(), params.getContext(),
cancelChecker);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,11 +464,6 @@ public DOMDocumentType getDoctype() {
return null;
}

/*
* (non-Javadoc)
*
* @see org.eclipse.lsp4xml.dom.Node#getOwnerDocument()
*/
@Override
public DOMDocument getOwnerDocument() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ public DOMNode(int start, int end) {
*
* @return the owner document and null otherwise.
*/
@Override
public DOMDocument getOwnerDocument() {
Node node = parent;
while (node != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ public static String getDocumentation(DOMAttr attr) {
doc.append(attr.getValue());
doc.append("**");
DOMElement element = attr.getOwnerElement();
if (XSDUtils.isComplexType(element)) {
if (XSDUtils.isXSComplexType(element)) {
doc.append(lineSeparator);
doc.append(" - Type: `Complex Type` ");
} else if (XSDUtils.isSimpleType(element)) {
} else if (XSDUtils.isXSSimpleType(element)) {
doc.append(lineSeparator);
doc.append(" - Type: `Simple Type` ");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import org.eclipse.lsp4xml.extensions.xsd.contentmodel.CMXSDContentModelProvider;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDCompletionParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDDefinitionParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDReferenceParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.diagnostics.XSDDiagnosticsParticipant;
import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant;
import org.eclipse.lsp4xml.services.extensions.IDefinitionParticipant;
import org.eclipse.lsp4xml.services.extensions.IReferenceParticipant;
import org.eclipse.lsp4xml.services.extensions.IXMLExtension;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lsp4xml.services.extensions.diagnostics.IDiagnosticsParticipant;
Expand All @@ -37,12 +39,15 @@ public class XSDPlugin implements IXMLExtension {

private final IDiagnosticsParticipant diagnosticsParticipant;

private final IReferenceParticipant referenceParticipant;

private XSDURIResolverExtension uiResolver;

public XSDPlugin() {
completionParticipant = new XSDCompletionParticipant();
definitionParticipant = new XSDDefinitionParticipant();
diagnosticsParticipant = new XSDDiagnosticsParticipant();
referenceParticipant = new XSDReferenceParticipant();
}

@Override
Expand Down Expand Up @@ -70,6 +75,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerCompletionParticipant(completionParticipant);
registry.registerDefinitionParticipant(definitionParticipant);
registry.registerDiagnosticsParticipant(diagnosticsParticipant);
registry.registerReferenceParticipant(referenceParticipant);
}

@Override
Expand All @@ -78,5 +84,6 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterCompletionParticipant(completionParticipant);
registry.unregisterDefinitionParticipant(definitionParticipant);
registry.unregisterDiagnosticsParticipant(diagnosticsParticipant);
registry.unregisterReferenceParticipant(referenceParticipant);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2019 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.lsp4xml.extensions.xsd.participants;

import java.util.List;

import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.ReferenceContext;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4xml.dom.DOMAttr;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.extensions.xsd.utils.XSDUtils;
import org.eclipse.lsp4xml.services.extensions.AbstractReferenceParticipant;
import org.eclipse.lsp4xml.utils.DOMUtils;
import org.eclipse.lsp4xml.utils.XMLPositionUtility;

/**
* XSD reference
*
* @author Angelo ZERR
*
*/
public class XSDReferenceParticipant extends AbstractReferenceParticipant {

@Override
protected boolean match(DOMDocument document) {
return DOMUtils.isXSD(document);
}

@Override
protected void findReferences(DOMNode node, Position position, int offset, ReferenceContext context,
List<Location> locations, CancelChecker cancelChecker) {
DOMAttr attr = node.findAttrAt(offset);
if (attr == null) {
return;
}

XSDUtils.collectXSReferenceTypes(attr, (from, to) -> {
locations.add(XMLPositionUtility.createLocation(from));
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
*******************************************************************************/
package org.eclipse.lsp4xml.extensions.xsd.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;

import org.eclipse.lsp4xml.dom.DOMAttr;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMElement;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.utils.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

Expand All @@ -29,6 +34,35 @@
*/
public class XSDUtils {

static class ReferencedNode {

private final DOMAttr node;
private BindingType referencedBindingType;
private boolean xsElement;

public ReferencedNode(DOMAttr node) {
this.node = node;
this.xsElement = isXSElement(node.getOwnerElement());
this.referencedBindingType = getReferencedBindingType(node);
}

public DOMNode getNode() {
return node;
}

public boolean isReference(String value, BindingType bindingType) {
if (referencedBindingType.equals(bindingType)
|| (referencedBindingType.isSimple() && bindingType.isSimple())
|| (referencedBindingType.isComplex() && bindingType.isComplex())
|| (xsElement && (BindingType.ELEMENT.equals(bindingType))
|| BindingType.REF.equals(bindingType))) {
return Objects.equal(value, node.getValue());
}
return false;
}

}

/**
* Binding type of xs attribute.
*
Expand Down Expand Up @@ -68,11 +102,11 @@ public static BindingType getBindingType(DOMAttr attr) {
DOMElement element = attr.getOwnerElement();
DOMElement parent = element.getParentElement();
if (parent != null) {
if (parent.getLocalName().equals("complexContent") | isComplexType(parent)) {
if (parent.getLocalName().equals("complexContent") || isXSComplexType(parent)) {
// parent element is complexContent or complexType -> bounded type is complex
return BindingType.COMPLEX;
}
if (parent.getLocalName().equals("simpleContent") || isSimpleType(parent)) {
if (parent.getLocalName().equals("simpleContent") || isXSSimpleType(parent)) {
// parent element is simpleContent or simpleType -> bounded type is simple
return BindingType.SIMPLE;
}
Expand All @@ -99,6 +133,18 @@ public static BindingType getBindingType(DOMAttr attr) {
return BindingType.NONE;
}

private static BindingType getReferencedBindingType(DOMAttr attr) {
Element element = attr.getOwnerElement();
if (isXSSimpleType(element)) {
return BindingType.SIMPLE;
} else if (isXSComplexType(element)) {
return BindingType.COMPLEX;
} else if (isXSElement(element) || isXSGroup(element)) {
return BindingType.REF;
}
return BindingType.NONE;
}

/**
* Collect XSD types declared in the XML Schema according the given attribute
* and binding type.
Expand Down Expand Up @@ -168,26 +214,98 @@ public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, b
}

private static boolean canCollectElement(DOMAttr originAttr, Element targetElement, BindingType bindingType) {
if (isComplexType(targetElement)) {
if (isXSComplexType(targetElement)) {
return bindingType.isComplex();
} else if (isSimpleType(targetElement)) {
} else if (isXSSimpleType(targetElement)) {
return bindingType.isSimple();
} else if (bindingType == BindingType.REF) {
// - xs:element/@name attributes if originAttr is xs:element/@ref
// - xs:group/@name attributes if originAttr is xs:group/@ref
return (originAttr.getOwnerElement().getLocalName().equals(targetElement.getLocalName()));
} else if (bindingType == BindingType.ELEMENT) {
return "element".equals(targetElement.getLocalName());
return isXSElement(targetElement);
}
return false;
}

public static boolean isComplexType(Element element) {
public static void collectXSReferenceTypes(DOMNode referencedNode, BiConsumer<DOMNode, DOMNode> collector) {
List<ReferencedNode> referencedNodes = new ArrayList<>();
Document document = referencedNode.getOwnerDocument();
switch (referencedNode.getNodeType()) {
case Node.ATTRIBUTE_NODE:
referencedNodes.add(new ReferencedNode((DOMAttr) referencedNode));
break;
case Node.DOCUMENT_NODE:
NodeList nodes = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node n = nodes.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
DOMElement element = (DOMElement) n;
if (isXSComplexType(element) || isXSSimpleType(element) || isXSElement(element)
|| isXSGroup(element)) {
DOMAttr attr = element.getAttributeNode("name");
if (attr != null && !StringUtils.isEmpty(attr.getValue())) {
referencedNodes.add(new ReferencedNode(attr));
}
}
}
}
}
if (referencedNodes.isEmpty()) {
return;
}

NodeList nodes = document.getDocumentElement().getChildNodes();
collectXSReferenceTypes(nodes, referencedNodes, collector);
}

private static void collectXSReferenceTypes(NodeList nodes, List<ReferencedNode> referencedNodes,
BiConsumer<DOMNode, DOMNode> collector) {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
DOMElement element = (DOMElement) node;
NamedNodeMap attributes = element.getAttributes();
if (attributes != null) {
for (int j = 0; j < attributes.getLength(); j++) {
DOMAttr attr = (DOMAttr) attributes.item(j);
BindingType bindingType = XSDUtils.getBindingType(attr);
if (bindingType != BindingType.NONE) {
String value = attr.getValue();
int index = value.indexOf(":");
if (index != -1) {
value = value.substring(index + 1, value.length());
}
for (ReferencedNode referencedNode : referencedNodes) {
if (referencedNode.isReference(value, bindingType)) {
collector.accept(attr, referencedNode.getNode());
}
}
}
}
}

}
if (node.hasChildNodes()) {
collectXSReferenceTypes(node.getChildNodes(), referencedNodes, collector);
}
}

}

public static boolean isXSComplexType(Element element) {
return "complexType".equals(element.getLocalName());
}

public static boolean isSimpleType(Element element) {
public static boolean isXSSimpleType(Element element) {
return "simpleType".equals(element.getLocalName());
}

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

public static boolean isXSGroup(Element element) {
return "group".equals(element.getLocalName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ public void checkCanceled() {
private final XMLDiagnostics diagnostics;
private final XMLFoldings foldings;
private final XMLDocumentLink documentLink;
private XMLDefinition definition;
private XMLReference reference;
private final XMLDefinition definition;
private final XMLReference reference;
private final XMLCodeActions codeActions;
private final XMLRename rename;

Expand Down Expand Up @@ -204,13 +204,14 @@ public List<DocumentLink> findDocumentLinks(DOMDocument document) {
return documentLink.findDocumentLinks(document);
}

public List<? extends LocationLink> findDefinition(DOMDocument xmlDocument, Position position, CancelChecker cancelChecker) {
public List<? extends LocationLink> findDefinition(DOMDocument xmlDocument, Position position,
CancelChecker cancelChecker) {
return definition.findDefinition(xmlDocument, position, cancelChecker);
}

public List<? extends Location> findReferences(DOMDocument xmlDocument, Position position,
ReferenceContext context) {
return reference.findReferences(xmlDocument, position, context);
public List<? extends Location> findReferences(DOMDocument xmlDocument, Position position, ReferenceContext context,
CancelChecker cancelChecker) {
return reference.findReferences(xmlDocument, position, context, cancelChecker);
}

public List<CodeAction> doCodeActions(CodeActionContext context, Range range, DOMDocument document,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.ReferenceContext;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.services.extensions.IReferenceParticipant;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;
Expand All @@ -32,10 +33,11 @@ public XMLReference(XMLExtensionsRegistry extensionsRegistry) {
this.extensionsRegistry = extensionsRegistry;
}

public List<? extends Location> findReferences(DOMDocument document, Position position, ReferenceContext context) {
public List<? extends Location> findReferences(DOMDocument document, Position position, ReferenceContext context,
CancelChecker cancelChecker) {
List<Location> locations = new ArrayList<>();
for (IReferenceParticipant participant : extensionsRegistry.getReferenceParticipants()) {
participant.findReference(document, position, context, locations);
participant.findReference(document, position, context, locations, cancelChecker);
}
return locations;
}
Expand Down
Loading

0 comments on commit fe77ac6

Please sign in to comment.