Skip to content

Commit

Permalink
DTD hover/completion support for documentation
Browse files Browse the repository at this point in the history
Fixes #585

Signed-off-by: Seiphon Wang <[email protected]>
  • Loading branch information
Seiphon authored and angelozerr committed Nov 8, 2019
1 parent 082780c commit b0dbc6b
Show file tree
Hide file tree
Showing 7 changed files with 1,055 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
*/
public class CMDTDAttributeDeclaration extends XMLAttributeDecl implements CMAttributeDeclaration {

private String documentation;
private CMDTDElementDeclaration elementDecl;

public CMDTDAttributeDeclaration(CMDTDElementDeclaration elementDecl) {
this.elementDecl = elementDecl;
}

@Override
public String getName() {
return super.name.localpart;
Expand All @@ -45,7 +52,11 @@ public Collection<String> getEnumerationValues() {

@Override
public String getDocumentation() {
return null;
if (documentation != null) {
return documentation;
}
documentation = elementDecl.getDocumentation(getName());
return documentation;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.xerces.impl.dtd.DTDGrammar;
import org.apache.xerces.impl.dtd.XMLDTDLoader;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.Grammar;
import org.apache.xerces.xni.parser.XMLInputSource;
Expand All @@ -43,13 +44,59 @@
*/
public class CMDTDDocument extends XMLDTDLoader implements CMDocument {


static class DTDNodeInfo {

private String comment;

public DTDNodeInfo() {
this.comment = null;
}

public String getComment() {
return comment;
}

public void setComment(String comment) {
this.comment = comment;
}
}

static class DTDElementInfo extends DTDNodeInfo {

private final Set<String> hierarchies;
private final Map<String, DTDNodeInfo> attributes;

public DTDElementInfo() {
this.hierarchies = new LinkedHashSet<String>();
this.attributes = new HashMap<>();
}

public Set<String> getHierarchies() {
return hierarchies;
}

public Map<String, DTDNodeInfo> getAttributes() {
return attributes;
}

public String getComment(String attrName) {
DTDNodeInfo attr = attributes.get(attrName);
return attr != null ? attr.getComment() : null;
}
}

private final String uri;

private Map<String, Set<String>> hierarchiesMap;
private Map<String, DTDElementInfo> hierarchiesMap;
private List<CMElementDeclaration> elements;
private DTDGrammar grammar;
private Set<String> hierarchies;
private FilesChangedTracker tracker;
private String comment;
private DTDElementInfo dtdElementInfo;
private Map<String, DTDNodeInfo> attributes;
private DTDNodeInfo nodeInfo;

public CMDTDDocument() {
this(null);
Expand Down Expand Up @@ -124,23 +171,55 @@ public void startContentModel(String elementName, Augmentations augs) throws XNI
if (hierarchiesMap == null) {
hierarchiesMap = new HashMap<>();
}
hierarchies = new LinkedHashSet<String>();
hierarchiesMap.put(elementName, hierarchies);
dtdElementInfo = new DTDElementInfo();
if (comment != null) {
dtdElementInfo.setComment(comment);
}
hierarchiesMap.put(elementName, dtdElementInfo);
super.startContentModel(elementName, augs);
}

@Override
public void element(String elementName, Augmentations augs) throws XNIException {
hierarchies = dtdElementInfo.getHierarchies();
hierarchies.add(elementName);
super.element(elementName, augs);
}

@Override
public void endContentModel(Augmentations augs) throws XNIException {
comment = null;
hierarchies = null;
super.endContentModel(augs);
}

@Override
public void startAttlist(String elementName, Augmentations augs) throws XNIException {
attributes = dtdElementInfo.getAttributes();
super.startAttlist(elementName, augs);
}

@Override
public void attributeDecl(String elementName, String attributeName, String type, String[] enumeration,
String defaultType, XMLString defaultValue, XMLString nonNormalizedDefaultValue, Augmentations augs)
throws XNIException {
if (comment != null) {
nodeInfo = new DTDNodeInfo();
nodeInfo.setComment(comment);
attributes.put(attributeName, nodeInfo);
}
super.attributeDecl(elementName, attributeName, type, enumeration, defaultType, defaultValue, nonNormalizedDefaultValue,
augs);
}

@Override
public void endAttlist(Augmentations augs) throws XNIException {
comment = null;
attributes = null;
nodeInfo = null;
super.endAttlist(augs);
}

@Override
public Grammar loadGrammar(XMLInputSource source) throws IOException, XNIException {
grammar = (DTDGrammar) super.loadGrammar(source);
Expand All @@ -164,11 +243,24 @@ public void loadInternalDTD(String internalSubset, String baseSystemId, String s
fDTDScanner.scanDTDInternalSubset(true, false, systemId != null);
}

@Override
public void comment(XMLString text, Augmentations augs) throws XNIException {
if (text != null) {
comment = text.toString();
}
super.comment(text, augs);
}

public Map<String, DTDElementInfo> getHierarchiesMap() {
return hierarchiesMap;
}

void collectElementsDeclaration(String elementName, List<CMElementDeclaration> elements) {
if (hierarchiesMap == null) {
return;
}
Set<String> children = hierarchiesMap.get(elementName);
DTDElementInfo elementInfo = hierarchiesMap.get(elementName);
Set<String> children = elementInfo.getHierarchies();
if (children == null) {
return;
}
Expand All @@ -184,7 +276,7 @@ void collectAttributesDeclaration(CMDTDElementDeclaration elementDecl, List<CMAt
int elementDeclIndex = grammar.getElementDeclIndex(elementDecl.name);
int index = grammar.getFirstAttributeDeclIndex(elementDeclIndex);
while (index != -1) {
CMDTDAttributeDeclaration attributeDecl = new CMDTDAttributeDeclaration();
CMDTDAttributeDeclaration attributeDecl = new CMDTDAttributeDeclaration(elementDecl);
grammar.getAttributeDecl(index, attributeDecl);
attributes.add(attributeDecl);
index = grammar.getNextAttributeDeclIndex(index);
Expand All @@ -200,4 +292,5 @@ public LocationLink findTypeLocation(DOMNode node) {
public boolean isDirty() {
return tracker != null ? tracker.isDirty() : null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.xerces.impl.dtd.XMLElementDecl;
import org.eclipse.lsp4xml.dom.DOMElement;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.extensions.dtd.contentmodel.CMDTDDocument.DTDElementInfo;
import org.eclipse.lsp4xml.extensions.dtd.contentmodel.CMDTDDocument.DTDNodeInfo;

/**
* DTD element declaration.
Expand All @@ -29,6 +32,7 @@ public class CMDTDElementDeclaration extends XMLElementDecl implements CMElement
private final CMDTDDocument document;
private List<CMElementDeclaration> elements;
private List<CMAttributeDeclaration> attributes;
private String documentation;

public CMDTDElementDeclaration(CMDTDDocument document, int index) {
this.document = document;
Expand Down Expand Up @@ -91,7 +95,28 @@ public CMAttributeDeclaration findCMAttribute(String attributeName) {

@Override
public String getDocumentation() {
return null;
if (documentation != null) {
return documentation;
}
Map<String, DTDElementInfo> hierarchiesMap = document.getHierarchiesMap();
if (hierarchiesMap != null) {
DTDElementInfo dtdElementInfo = hierarchiesMap.get(getName());
documentation = dtdElementInfo.getComment();
}
return documentation;
}

public String getDocumentation(String attrName) {
Map<String, DTDElementInfo> hierarchiesMap = document.getHierarchiesMap();
if (hierarchiesMap != null) {
DTDElementInfo dtdElementInfo = hierarchiesMap.get(getName());
Map<String, DTDNodeInfo> attributesMap = dtdElementInfo.getAttributes();
DTDNodeInfo nodeInfo = attributesMap.get(attrName);
if (nodeInfo != null) {
documentation = nodeInfo.getComment();
}
}
return documentation;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ public void testCompletionDocumentationWithSource() throws BadLocationException
" \"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd\">\r\n" + //
"\r\n" + //
" <|";
testCompletionFor(xml, c("catalog", te(5, 2, 5, 3, "<catalog>$1</catalog>$0"), "<catalog", "Source: catalog.dtd", MarkupKind.PLAINTEXT));
testCompletionFor(xml, c("catalog", te(5, 2, 5, 3, "<catalog>$1</catalog>$0"), "<catalog", " $Id: catalog.dtd,v 1.10 2002/10/18 23:54:58 ndw Exp $ "
+
System.lineSeparator() +
System.lineSeparator() + "Source: catalog.dtd", MarkupKind.PLAINTEXT));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2018 Angelo ZERR and Liferay 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
*
* Contributors:
* Angelo Zerr <[email protected]> - initial API and implementation
* Seiphon Wang <[email protected]>
*/
package org.eclipse.lsp4xml.extensions.contentmodel;

import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.util.URI.MalformedURIException;
import org.eclipse.lsp4xml.XMLAssert;
import org.eclipse.lsp4xml.commons.BadLocationException;
import org.eclipse.lsp4xml.services.XMLLanguageService;
import org.junit.Test;

public class DTDHoverExtensionsTest {

@Test
public void testTagHover() throws BadLocationException, MalformedURIException {
String dtdURI = getDTDFileURI("liferay-service-builder_7_2_0.dtd");
String xml = "<?xml version=\"1.0\"?>\r\n" + //
"<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">"
+ "<service-builder dependency-injector=\"ds\" package-path=\"testSB\"></servi|ce-builder>";
assertHover(xml,"The service-builder element is the root of the deployment descriptor for" + //
" a Service Builder descriptor that is used to generate services available to" +
" portlets. The Service Builder saves the developer time by generating Spring" +
" utilities, SOAP utilities, and Hibernate persistence classes to ease the" +
" development of services."
+ //
System.lineSeparator() + //
System.lineSeparator() + "Source: [liferay-service-builder_7_2_0.dtd](" + dtdURI + ")",
206);
}

@Test
public void testAttributeNameHover() throws BadLocationException, MalformedURIException {
String dtdURI = getDTDFileURI("liferay-service-builder_7_2_0.dtd");
String xml = "<?xml version=\"1.0\"?>\r\n" + //
"<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">"
+ "<service-builder dependency-injector=\"ds\" pa|ckage-path=\"testSB\"></service-builder>";
assertHover(xml,
"The package-path value specifies the package of the generated code."
+ //
System.lineSeparator() + //
System.lineSeparator() + "Source: [liferay-service-builder_7_2_0.dtd](" + dtdURI + ")",
null);
}

private static void assertHover(String value, String expectedHoverLabel, Integer expectedHoverOffset)
throws BadLocationException {
XMLAssert.assertHover(new XMLLanguageService(), value, "src/test/resources/catalogs/catalog-liferay.xml", null,
expectedHoverLabel, expectedHoverOffset);
}

private static String getDTDFileURI(String dtdURI) throws MalformedURIException {
return XMLEntityManager.expandSystemId("dtd/" + dtdURI, "src/test/resources/test.xml", true).replace("///",
"/");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system
systemId="http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd"
uri="../dtd/liferay-service-builder_7_2_0.dtd" />
</catalog>
Loading

0 comments on commit b0dbc6b

Please sign in to comment.