You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/05/07 22:47:42 UTC
[groovy] branch master updated: GROOVY-9543: Groovydoc generics
improvements(closes #1239)
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 285d3ab GROOVY-9543: Groovydoc generics improvements(closes #1239)
285d3ab is described below
commit 285d3ab3b49d4a4971ed6d13dcd08e8ac177e9f7
Author: Mikko Värri <vm...@linuxbox.fi>
AuthorDate: Fri May 8 06:46:46 2020 +0800
GROOVY-9543: Groovydoc generics improvements(closes #1239)
---
.../org/apache/groovy/antlr/GroovydocVisitor.java | 33 +++---
.../tools/groovydoc/SimpleGroovyClassDoc.java | 10 +-
.../groovydoc/antlr4/GroovydocJavaVisitor.java | 5 +-
.../gstringTemplates/classLevel/classDocName.html | 3 +-
.../groovy/tools/groovydoc/GroovyDocToolTest.java | 131 ++++++++++++++++++++-
.../groovydoc/testfiles/generics/Groovy.groovy | 22 ++++
.../tools/groovydoc/testfiles/generics/Java.java | 21 ++++
7 files changed, 206 insertions(+), 19 deletions(-)
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java b/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java
index 5fac475..4275e03 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java
@@ -33,9 +33,11 @@ import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.control.ResolveVisitor;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.groovydoc.GroovyClassDoc;
import org.codehaus.groovy.groovydoc.GroovyMethodDoc;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.tools.groovydoc.LinkArgument;
import org.codehaus.groovy.tools.groovydoc.SimpleGroovyAnnotationRef;
import org.codehaus.groovy.tools.groovydoc.SimpleGroovyClassDoc;
@@ -100,7 +102,7 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
if (node instanceof InnerClassNode) {
name = name.replace('$', '.');
}
- currentClassDoc = new SimpleGroovyClassDoc(imports, aliases, name, links);
+ currentClassDoc = new SimpleGroovyClassDoc(withDefaultImports(imports), aliases, name, links);
if (node.isEnum()) {
currentClassDoc.setTokenType(SimpleGroovyDoc.ENUM_DEF);
} else if (node.isAnnotationDefinition()) {
@@ -158,6 +160,15 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
}
}
+ private List<String> withDefaultImports(List<String> imports) {
+ imports = imports != null ? imports : new ArrayList<>();
+ imports.add(packagePath + "/*"); // everything in this package
+ for (String pkg : ResolveVisitor.DEFAULT_IMPORTS) {
+ imports.add(pkg.replace('.', '/') + "*");
+ }
+ return imports;
+ }
+
private static final Pattern JAVADOC_COMMENT_PATTERN = Pattern.compile("(?s)/\\*\\*(.*?)\\*/");
private String getDocContent(Groovydoc groovydoc) {
@@ -205,19 +216,9 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
}
private String genericTypesAsString(GenericsType[] genericsTypes) {
- if (genericsTypes == null) return "";
- StringBuilder result = new StringBuilder("<");
- boolean first = true;
- for (GenericsType genericsType : genericsTypes) {
- if (!first) {
- result.append(", ");
- } else {
- first = false;
- }
- result.append(genericsType.getName());
- }
- result.append(">");
- return result.toString();
+ if (genericsTypes == null || genericsTypes.length == 0)
+ return "";
+ return "<" + DefaultGroovyMethods.join(genericsTypes, ", ") + ">";
}
private void processPropertiesFromGetterSetter(SimpleGroovyMethodDoc currentMethodDoc) {
@@ -298,7 +299,9 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
}
private String makeType(ClassNode node) {
- return node.getName().replace('.', '/').replace('$', '.');
+ return node.getName().replace('.', '/').replace('$', '.')
+ + genericTypesAsString(node.getGenericsTypes())
+ ;
}
@Override
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java
index ef29024..d0ab5d9 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java
@@ -74,6 +74,8 @@ public class SimpleGroovyClassDoc extends SimpleGroovyAbstractableElementDoc imp
TAG_TEXT.put("author", "Authors");
TAG_TEXT.put("version", "Version");
TAG_TEXT.put("default", "Default");
+ // typeparam is used internally as a specialization of param to separate type params from regular params.
+ TAG_TEXT.put("typeparam", "Type Parameters");
}
private final List<GroovyConstructorDoc> constructors;
private final List<GroovyFieldDoc> fields;
@@ -930,7 +932,13 @@ public class SimpleGroovyClassDoc extends SimpleGroovyAbstractableElementDoc imp
} else if ("param".equals(tagname)) {
int index = content.indexOf(' ');
if (index >= 0) {
- content = "<code>" + content.substring(0, index) + "</code> - " + content.substring(index);
+ String paramName = content.substring(0, index);
+ String paramDesc = content.substring(index);
+ if (paramName.startsWith("<") && paramName.endsWith(">")) {
+ paramName = paramName.substring(1, paramName.length() - 1);
+ tagname = "typeparam";
+ }
+ content = "<code>" + paramName + "</code> - " + paramDesc;
}
}
if (TAG_TEXT.containsKey(tagname)) {
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
index d06f4ca..834c068 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
@@ -185,7 +185,9 @@ public class GroovydocJavaVisitor extends VoidVisitorAdapter<Object> {
}
private String genericTypesAsString(NodeList<TypeParameter> typeParameters) {
- return DefaultGroovyMethods.join(typeParameters, ", ");
+ if (typeParameters == null || typeParameters.size() == 0)
+ return "";
+ return "<" + DefaultGroovyMethods.join(typeParameters, ", ") + ">";
}
private SimpleGroovyClassDoc visit(TypeDeclaration<?> n) {
@@ -254,6 +256,7 @@ public class GroovydocJavaVisitor extends VoidVisitorAdapter<Object> {
@Override
public void visit(MethodDeclaration m, Object arg) {
SimpleGroovyMethodDoc meth = new SimpleGroovyMethodDoc(m.getNameAsString(), currentClassDoc);
+ meth.setTypeParameters(genericTypesAsString(m.getTypeParameters()));
meth.setReturnType(makeType(m.getType()));
setConstructorOrMethodCommon(m, meth);
currentClassDoc.add(meth);
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
index acb3c89..2b8f5ea 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
@@ -80,7 +80,8 @@
def isRequired = { f, v -> def req = f.constantValueExpression() == null; req.toString() == v }
def upcase = { n -> n[0].toUpperCase() + n[1..-1] }
def paramsOf = { n, boolean brief -> n.parameters().collect{ param -> (brief?'':annotations(param, ' ')) + linkable(param.isTypeAvailable()?param.type():param.typeName()) + (param.vararg()?'... ':' ') + param.name() + (param.defaultValue() ? " = " + param.defaultValue():"") }.join(", ") }
- def nameFromParams = { n -> n.name() + '(' + n.parameters().collect{ param -> param.isTypeAvailable()?param.type().qualifiedTypeName():param.typeName() }.join(', ') + ')' }
+ def rawType = { n -> def g = n.indexOf("<"); g >= 0 ? n.substring(0, g) : n }
+ def nameFromParams = { n -> n.name() + '(' + n.parameters().collect{ param -> rawType(param.isTypeAvailable()?param.type().qualifiedTypeName():param.typeName()) }.join(', ') + ')' }
def nameFromJavaParams = { n -> n.getName() + '(' + n.parameterTypes.collect{ param -> param.getName() }.join(', ') + ')' }
%>
<html>
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
index 1f8c138..6fcec54 100644
--- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
@@ -31,7 +31,6 @@ import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
public class GroovyDocToolTest extends GroovyTestCase {
private static final String MOCK_DIR = "mock/doc";
@@ -721,6 +720,136 @@ public class GroovyDocToolTest extends GroovyTestCase {
assertEquals("The constructor parameter link text should be Foo", "Foo", constructor.group(3));
}
+ public void testJavaGenericsTitle() throws Exception {
+ final String base = "org/codehaus/groovy/tools/groovydoc/testfiles/generics";
+ htmlTool.add(Arrays.asList(
+ base + "/Java.java"
+ ));
+
+ final MockOutputTool output = new MockOutputTool();
+ htmlTool.renderToOutput(output, MOCK_DIR);
+
+ final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html");
+
+ final Matcher title = Pattern.compile(Pattern.quote(
+ "<h2 title=\"[Java] Class Java<N extends Number & Comparable<? extends Number>>\" class=\"title\">"+
+ "[Java] Class Java<N extends Number & Comparable<? extends Number>></h2>"
+ )).matcher(javadoc);
+
+ assertTrue("The title should have the generics information", title.find());
+ }
+
+ public void testGroovyGenericsTitle() throws Exception {
+ final String base = "org/codehaus/groovy/tools/groovydoc/testfiles/generics";
+ htmlTool.add(Arrays.asList(
+ base + "/Groovy.groovy"
+ ));
+
+ final MockOutputTool output = new MockOutputTool();
+ htmlTool.renderToOutput(output, MOCK_DIR);
+
+ final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
+
+ final Matcher title = Pattern.compile(Pattern.quote(
+ "<h2 title=\"[Groovy] Trait Groovy<N extends Number & Comparable<? extends Number>>\" class=\"title\">"+
+ "[Groovy] Trait Groovy<N extends Number & Comparable<? extends Number>></h2>"
+ )).matcher(groovydoc);
+
+ assertTrue("The title should have the generics information", title.find());
+ }
+
+ public void testParamTagForTypeParams() throws Exception {
+ final String base = "org/codehaus/groovy/tools/groovydoc/testfiles/generics";
+ htmlTool.add(Arrays.asList(
+ base + "/Java.java",
+ base + "/Groovy.groovy"
+ ));
+
+ final MockOutputTool output = new MockOutputTool();
+ htmlTool.renderToOutput(output, MOCK_DIR);
+
+ final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html");
+ final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
+
+ final Pattern classTypeParams = Pattern.compile(
+ "<DL><DT><B>Type Parameters:</B></DT><DD><code>N</code> - Doc.</DD></DL>"
+ );
+ final Pattern methodTypeParams = Pattern.compile(
+ "<DL><DT><B>Type Parameters:</B></DT><DD><code>A</code> - Doc.</DD><DD><code>B</code> - Doc.</DD></DL>"
+ );
+
+ assertTrue("The Java class doc should have type parameters definitions", classTypeParams.matcher(javadoc).find());
+ assertTrue("The Groovy class doc should have type parameters definitions", classTypeParams.matcher(groovydoc).find());
+ assertTrue("The Java method doc should have type parameters definitions", methodTypeParams.matcher(javadoc).find());
+ assertTrue("The Groovy method doc should have type parameters definitions", methodTypeParams.matcher(groovydoc).find());
+ }
+
+ public void testMethodTypeParams() throws Exception {
+ final String base = "org/codehaus/groovy/tools/groovydoc/testfiles/generics";
+ htmlTool.add(Arrays.asList(
+ base + "/Java.java",
+ base + "/Groovy.groovy"
+ ));
+
+ final MockOutputTool output = new MockOutputTool();
+ htmlTool.renderToOutput(output, MOCK_DIR);
+
+ final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html");
+ final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
+
+ final Pattern methodSummaryTypeParams = Pattern.compile(
+ "<td class=\"colFirst\"><code><A, B></code></td>"
+ );
+ final Pattern methodDetailsTypeParams = Pattern.compile(
+ "<h4><A, B> (public )?static int <strong>compare</strong>"
+ );
+
+ assertTrue("The Java method summary should have type parameters", methodSummaryTypeParams.matcher(javadoc).find());
+ assertTrue("The Groovy method summary should have type parameters", methodSummaryTypeParams.matcher(groovydoc).find());
+ assertTrue("The Java method details should have type parameters", methodDetailsTypeParams.matcher(javadoc).find());
+ assertTrue("The Groovy method details should have type parameters", methodDetailsTypeParams.matcher(groovydoc).find());
+ }
+
+ public void testMethodParamTypeParams() throws Exception {
+ final String base = "org/codehaus/groovy/tools/groovydoc/testfiles/generics";
+ htmlTool.add(Arrays.asList(
+ base + "/Java.java",
+ base + "/Groovy.groovy"
+ ));
+
+ final MockOutputTool output = new MockOutputTool();
+ htmlTool.renderToOutput(output, MOCK_DIR);
+
+ final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html");
+ final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
+
+ final Pattern methodSummary = Pattern.compile(Pattern.quote(
+ "<code><strong><a href=\"#compare(Class, Class)\">compare</a></strong>"
+ + "("
+ + "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a><A> a, "
+ + "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a><B> b"
+ + ")"
+ + "</code>"
+ ));
+ final Pattern methodDetailAnchor = Pattern.compile(Pattern.quote(
+ "<a name=\"compare(Class, Class)\"><!-- --></a>"
+ ));
+ final Pattern methodDetailTitle = Pattern.compile(Pattern.quote(
+ "<strong>compare</strong>" +
+ "(" +
+ "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a><A> a, " +
+ "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a><B> b" +
+ ")"
+ ));
+
+ assertTrue("The Java method summary should include type parameters", methodSummary.matcher(javadoc).find());
+ assertTrue("The Java method detail anchor should NOT include type parameters", methodDetailAnchor.matcher(javadoc).find());
+ assertTrue("The Java method detail title should include type parameters", methodDetailTitle.matcher(javadoc).find());
+ assertTrue("The Groovy method summary should include type parameters", methodSummary.matcher(groovydoc).find());
+ assertTrue("The Groovy method detail anchor should NOT include type parameters", methodDetailAnchor.matcher(groovydoc).find());
+ assertTrue("The Groovy method detail title should include type parameters", methodDetailTitle.matcher(groovydoc).find());
+ }
+
public void testScript() throws Exception {
List<String> srcList = new ArrayList<String>();
srcList.add("org/codehaus/groovy/tools/groovydoc/testfiles/Script.groovy");
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy
new file mode 100644
index 0000000..0afe01b
--- /dev/null
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy
@@ -0,0 +1,22 @@
+package org.codehaus.groovy.tools.groovydoc.testfiles.generics
+
+/**
+ * Generic class.
+ *
+ * @param <N> Doc.
+ */
+trait Groovy<N extends Number & Comparable<? extends Number>> {
+
+ /**
+ * Generic method.
+ *
+ * @param <A> Doc.
+ * @param <B> Doc.
+ * @param a Doc.
+ * @param b Doc.
+ * @return Doc.
+ */
+ static <A, B> int compare(Class<A> a, Class<B> b) {
+ 0
+ }
+}
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java
new file mode 100644
index 0000000..e2a1d3d
--- /dev/null
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java
@@ -0,0 +1,21 @@
+package org.codehaus.groovy.tools.groovydoc.testfiles.generics;
+
+/**
+ * Generic class.
+ *
+ * @param <N> Doc.
+ */
+public abstract class Java<N extends Number & Comparable<? extends Number>> {
+ /**
+ * Generic method.
+ *
+ * @param <A> Doc.
+ * @param <B> Doc.
+ * @param a Doc.
+ * @param b Doc.
+ * @return Doc.
+ */
+ public static <A, B> int compare(Class<A> a, Class<B> b) {
+ return 0;
+ }
+}