Skip to content

Commit 42c4e1a

Browse files
committed
WIP: open-api: parse javadoc
- Java APT can't be used to read doc bc we use byte code analysis - Use checkstyle for getting doc - This feature will be only for Java code base (not available on Kotlin) - ref #3729
1 parent d7bfaf8 commit 42c4e1a

File tree

12 files changed

+756
-8
lines changed

12 files changed

+756
-8
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ TODO
2828
.interp
2929
tmp
3030
checkstyle
31-
javadoc
3231
*.mv.db
3332
versions
3433
out

modules/jooby-openapi/pom.xml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@
6464
<artifactId>swagger-models</artifactId>
6565
</dependency>
6666

67+
<dependency>
68+
<groupId>com.puppycrawl.tools</groupId>
69+
<artifactId>checkstyle</artifactId>
70+
<version>10.26.1</version>
71+
</dependency>
72+
6773
<dependency>
6874
<groupId>commons-codec</groupId>
6975
<artifactId>commons-codec</artifactId>
@@ -134,13 +140,6 @@
134140
<version>1.17.6</version>
135141
<scope>test</scope>
136142
</dependency>
137-
138-
<dependency>
139-
<groupId>com.puppycrawl.tools</groupId>
140-
<artifactId>checkstyle</artifactId>
141-
<version>10.26.1</version>
142-
<scope>test</scope>
143-
</dependency>
144143
</dependencies>
145144

146145
<build>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.openapi.javadoc;
7+
8+
import static io.jooby.internal.openapi.javadoc.JavaDocSupport.*;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.Objects;
13+
import java.util.Optional;
14+
import java.util.stream.Collectors;
15+
16+
import com.puppycrawl.tools.checkstyle.api.DetailAST;
17+
import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
18+
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
19+
20+
public class ClassDoc extends JavaDocNode {
21+
22+
private final DetailAST node;
23+
private List<MethodDoc> methods = new ArrayList<>();
24+
25+
public ClassDoc(DetailAST node, DetailAST javaDoc) {
26+
super(javaDoc);
27+
this.node = node;
28+
}
29+
30+
public void addMethod(MethodDoc method) {
31+
this.methods.add(method);
32+
}
33+
34+
public String getSummary() {
35+
var text = new StringBuilder();
36+
for (var node : forward(javadoc, STOP_TOKENS).toList()) {
37+
if (node.getType() == JavadocTokenTypes.NEWLINE && !text.isEmpty()) {
38+
break;
39+
} else if (node.getType() == JavadocTokenTypes.TEXT) {
40+
text.append(node.getText());
41+
}
42+
}
43+
return text.isEmpty() ? getText().trim() : text.toString().trim();
44+
}
45+
46+
public String getDescription() {
47+
var text = getText();
48+
var summary = getSummary();
49+
return summary.equals(text) ? "" : text.replaceAll(summary, "").trim();
50+
}
51+
52+
public Optional<MethodDoc> getMethod(String name, List<String> parameterNames) {
53+
var filtered = methods.stream().filter(it -> it.getName().equals(name)).toList();
54+
if (filtered.isEmpty()) {
55+
return Optional.empty();
56+
}
57+
if (filtered.size() == 1) {
58+
return Optional.of(filtered.get(0));
59+
}
60+
return filtered.stream()
61+
.filter(it -> it.getParameterNames().equals(parameterNames))
62+
.findFirst();
63+
}
64+
65+
public String getSimpleName() {
66+
return node.findFirstToken(TokenTypes.IDENT).getText();
67+
}
68+
69+
public String getName() {
70+
return forward(node.getParent())
71+
.filter(tokens(TokenTypes.PACKAGE_DEF))
72+
.map(
73+
it ->
74+
tree(it)
75+
.filter(tokens(TokenTypes.DOT, TokenTypes.IDENT))
76+
.findFirst()
77+
.orElse(null))
78+
.filter(Objects::nonNull)
79+
.flatMap(
80+
it ->
81+
tree(it)
82+
.filter(tokens(TokenTypes.DOT, TokenTypes.SEMI).negate())
83+
.map(DetailAST::getText))
84+
.collect(Collectors.joining(".", "", "."))
85+
+ getSimpleName();
86+
}
87+
88+
public List<MethodDoc> getMethods() {
89+
return methods;
90+
}
91+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.openapi.javadoc;
7+
8+
import java.util.List;
9+
import java.util.Set;
10+
11+
import com.puppycrawl.tools.checkstyle.DetailNodeTreeStringPrinter;
12+
import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser;
13+
import com.puppycrawl.tools.checkstyle.api.DetailAST;
14+
import com.puppycrawl.tools.checkstyle.api.DetailNode;
15+
import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
16+
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
17+
18+
public class JavaDocNode {
19+
protected final DetailNode javadoc;
20+
protected static final Set<Integer> STOP_TOKENS = Set.of(JavadocTokenTypes.JAVADOC_TAG);
21+
22+
public JavaDocNode(DetailAST node) {
23+
this.javadoc = new JavadocDetailNodeParser().parseJavadocAsDetailNode(node).getTree();
24+
}
25+
26+
public String getText() {
27+
return getText(JavaDocSupport.forward(javadoc, STOP_TOKENS).toList(), false);
28+
}
29+
30+
protected String getText(List<DetailNode> nodes, boolean stripLeading) {
31+
var builder = new StringBuilder();
32+
for (var node : nodes) {
33+
if (node.getType() == JavadocTokenTypes.TEXT) {
34+
var text = node.getText();
35+
if (stripLeading && Character.isWhitespace(text.charAt(0))) {
36+
builder.append(' ').append(text.stripLeading());
37+
} else {
38+
builder.append(text);
39+
}
40+
} else if (node.getType() == JavadocTokenTypes.NEWLINE) {
41+
var next = JavadocUtil.getNextSibling(node);
42+
if (next != null && next.getType() != JavadocTokenTypes.LEADING_ASTERISK) {
43+
builder.append(next.getText());
44+
}
45+
}
46+
}
47+
return builder.toString().trim();
48+
}
49+
50+
@Override
51+
public String toString() {
52+
return DetailNodeTreeStringPrinter.printTree(javadoc, "", "");
53+
}
54+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.openapi.javadoc;
7+
8+
import static io.jooby.internal.openapi.javadoc.JavaDocSupport.backward;
9+
import static io.jooby.internal.openapi.javadoc.JavaDocSupport.tokens;
10+
11+
import java.io.IOException;
12+
import java.nio.file.Path;
13+
import java.util.Optional;
14+
15+
import com.puppycrawl.tools.checkstyle.JavaParser;
16+
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
17+
import com.puppycrawl.tools.checkstyle.api.DetailAST;
18+
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
19+
20+
public class JavaDocParser {
21+
22+
public static Optional<ClassDoc> parse(Path filePath) throws CheckstyleException, IOException {
23+
ClassDoc result = null;
24+
var tree = JavaParser.parseFile(filePath.toFile(), JavaParser.Options.WITH_COMMENTS);
25+
for (var comment :
26+
JavaDocSupport.forward(tree).filter(tokens(TokenTypes.COMMENT_CONTENT)).toList()) {
27+
var nodePath = path(comment);
28+
// ensure class
29+
if (result == null) {
30+
result = new ClassDoc(nodePath[1], comment.getParent());
31+
}
32+
if (nodePath[nodePath.length - 1] != null) {
33+
// there is a method here
34+
var method = new MethodDoc(nodePath[nodePath.length - 1], comment.getParent());
35+
result.addMethod(method);
36+
}
37+
}
38+
return Optional.ofNullable(result);
39+
}
40+
41+
private static DetailAST[] path(DetailAST comment) {
42+
var classDef =
43+
backward(comment)
44+
.filter(
45+
tokens(
46+
TokenTypes.ENUM_DEF,
47+
TokenTypes.CLASS_DEF,
48+
TokenTypes.INTERFACE_DEF,
49+
TokenTypes.RECORD_DEF))
50+
.findFirst()
51+
.orElseThrow(() -> new IllegalStateException("no type found"));
52+
var packageDef =
53+
JavaDocSupport.forward(classDef.getParent())
54+
.filter(tokens(TokenTypes.PACKAGE_DEF))
55+
.findFirst()
56+
.orElse(null);
57+
var methodDef =
58+
backward(comment).filter(tokens(TokenTypes.METHOD_DEF)).findFirst().orElse(null);
59+
return new DetailAST[] {packageDef, classDef, methodDef};
60+
}
61+
}

0 commit comments

Comments
 (0)