Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DTD hover/completion support for documentation #592

Merged
merged 1 commit into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a XML catalog. Please update this file with

<?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>

To check that the XML catalog is working and uses the local liferay-service-builder_7_2_0.dtd, disconnect your Internet and try to execute your test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just got it. the pr has been updated.

<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