Skip to content

Commit 2f5c598

Browse files
committed
open-api: Strong lookup of MVC method
- use types to find a method
1 parent 55e4e4c commit 2f5c598

File tree

8 files changed

+96
-65
lines changed

8 files changed

+96
-65
lines changed

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/AnnotationParser.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,12 @@ public static List<OperationExt> parse(ParserContext ctx, String prefix, Type ty
279279
doc -> {
280280
JavaDocSetter.setPath(operationExt, doc);
281281
var parameterNames =
282-
Optional.ofNullable(operationExt.getNode().parameters)
283-
.orElse(List.of())
284-
.stream()
285-
.map(p -> p.name)
282+
operationExt.getNode().parameters.stream().map(p -> p.name).toList();
283+
var parameterTypes =
284+
Stream.of(Type.getArgumentTypes(operationExt.getNode().desc))
285+
.map(Type::getClassName)
286286
.toList();
287-
doc.getMethod(operationExt.getOperationId(), parameterNames)
287+
doc.getMethod(operationExt.getOperationId(), parameterTypes)
288288
.ifPresent(
289289
methodDoc -> JavaDocSetter.set(operationExt, methodDoc, parameterNames));
290290
});

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/javadoc/ClassDoc.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,11 @@ private void defaultRecordMembers() {
117117
/* Virtual method */
118118
var method =
119119
new MethodDoc(
120-
context, createVirtualMember(name.getText(), TokenTypes.METHOD_DEF), memberDoc);
120+
context,
121+
createVirtualMember(name.getText(), TokenTypes.METHOD_DEF),
122+
memberDoc)
123+
.markAsVirtual();
124+
121125
addMethod(method);
122126
});
123127
}
@@ -142,7 +146,7 @@ private void defaultEnumMembers() {
142146
}
143147
}
144148

145-
private static DetailAstImpl createVirtualMember(String name, int tokenType) {
149+
private DetailAstImpl createVirtualMember(String name, int tokenType) {
146150
var publicMod = new DetailAstImpl();
147151
publicMod.initialize(
148152
TokenTypes.LITERAL_PUBLIC, TokenUtil.getTokenName(TokenTypes.LITERAL_PUBLIC));
@@ -174,8 +178,8 @@ public Optional<FieldDoc> getField(String name) {
174178
return Optional.ofNullable(fields.get(name));
175179
}
176180

177-
public Optional<MethodDoc> getMethod(String name, List<String> parameterNames) {
178-
return Optional.ofNullable(methods.get(toMethodSignature(name, parameterNames)));
181+
public Optional<MethodDoc> getMethod(String name, List<String> types) {
182+
return Optional.ofNullable(methods.get(toMethodSignature(name, types)));
179183
}
180184

181185
public Optional<ScriptDoc> getScript(String method, String pattern) {
@@ -191,11 +195,11 @@ private String toScriptSignature(String method, String pattern) {
191195
}
192196

193197
private String toMethodSignature(MethodDoc method) {
194-
return toMethodSignature(method.getName(), method.getParameterNames());
198+
return toMethodSignature(method.getName(), method.getParameterTypes());
195199
}
196200

197-
private String toMethodSignature(String methodName, List<String> parameterNames) {
198-
return methodName + parameterNames.stream().collect(Collectors.joining(", ", "(", ")"));
201+
private String toMethodSignature(String methodName, List<String> types) {
202+
return methodName + types.stream().collect(Collectors.joining(", ", "(", ")"));
199203
}
200204

201205
public String getSimpleName() {

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/javadoc/JavaDocParser.java

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static io.jooby.SneakyThrows.throwingFunction;
1010
import static io.jooby.internal.openapi.javadoc.JavaDocStream.*;
1111
import static io.jooby.internal.openapi.javadoc.JavaDocStream.tokens;
12+
import static io.jooby.internal.openapi.javadoc.JavaDocSupport.*;
1213
import static java.util.Optional.ofNullable;
1314

1415
import java.io.File;
@@ -19,7 +20,6 @@
1920
import java.util.concurrent.atomic.AtomicInteger;
2021
import java.util.function.BiConsumer;
2122
import java.util.function.Predicate;
22-
import java.util.stream.Collectors;
2323

2424
import com.puppycrawl.tools.checkstyle.JavaParser;
2525
import com.puppycrawl.tools.checkstyle.api.DetailAST;
@@ -47,15 +47,9 @@ public Optional<ClassDoc> parse(String typeName) {
4747

4848
public Map<String, ClassDoc> traverse(DetailAST tree) {
4949
var classes = new HashMap<String, ClassDoc>();
50-
var types =
51-
tokens(
52-
TokenTypes.ENUM_DEF,
53-
TokenTypes.CLASS_DEF,
54-
TokenTypes.INTERFACE_DEF,
55-
TokenTypes.RECORD_DEF);
5650
traverse(
5751
tree,
58-
types,
52+
TYPES,
5953
modifiers -> tree(modifiers).noneMatch(tokens(TokenTypes.LITERAL_PRIVATE)),
6054
(scope, comment) -> {
6155
var counter = new AtomicInteger(0);
@@ -70,7 +64,7 @@ public Map<String, ClassDoc> traverse(DetailAST tree) {
7064
(member, memberComment) -> {
7165
counter.addAndGet(memberComment == JavaDocNode.EMPTY_AST ? 0 : 1);
7266
// check member belong to current scope
73-
if (scope == backward(member).filter(types).findFirst().orElse(null)) {
67+
if (scope == backward(member).filter(TYPES).findFirst().orElse(null)) {
7468
if (member.getType() == TokenTypes.VARIABLE_DEF) {
7569
classDoc.addField(new FieldDoc(this, member, memberComment));
7670
} else {
@@ -183,15 +177,15 @@ private ScriptRef resolveScriptComment(
183177
}
184178

185179
private ScriptRef resolveFromMethodRef(ClassDoc classDoc, DetailAST methodRef) {
186-
var referenceOwner = getTypeName(methodRef);
180+
var referenceOwner = getQualifiedName(methodRef);
187181
DetailAST scope = null;
188182
String className;
189183
if (referenceOwner.equals("this")) {
190184
scope = classDoc.getNode();
191185
className = classDoc.getName();
192186
} else {
193187
// resolve className
194-
className = JavaDocSupport.toQualifiedName(classDoc.node, referenceOwner);
188+
className = toQualifiedName(classDoc.node, referenceOwner);
195189
scope = resolveType(className);
196190
if (scope == JavaDocNode.EMPTY_AST) {
197191
// not found
@@ -219,7 +213,7 @@ private ScriptRef resolveFromMethodRef(ClassDoc classDoc, DetailAST methodRef) {
219213
.filter(tokens(TokenTypes.PARAMETER_DEF))
220214
.findFirst()
221215
.flatMap(p -> children(p).filter(tokens(TokenTypes.TYPE)).findFirst())
222-
.filter(type -> getTypeName(type).equals("Context"))
216+
.filter(type -> getQualifiedName(type).equals("Context"))
223217
.isPresent())
224218
.findFirst()
225219
.orElseThrow(
@@ -234,13 +228,6 @@ private ScriptRef resolveFromMethodRef(ClassDoc classDoc, DetailAST methodRef) {
234228
.orElseGet(() -> new ScriptRef(null, JavaDocNode.EMPTY_AST));
235229
}
236230

237-
private static String getTypeName(DetailAST methodRef) {
238-
return tree(methodRef.getFirstChild())
239-
.filter(tokens(TokenTypes.DOT).negate())
240-
.map(DetailAST::getText)
241-
.collect(Collectors.joining("."));
242-
}
243-
244231
/**
245232
* ELIST -> EXPR -> STRING_LITERAL
246233
*

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/javadoc/JavaDocStream.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,6 @@ public static Stream<DetailNode> backward(DetailNode node) {
8080
return backward(ASTNode.javadoc(node));
8181
}
8282

83-
public static Stream<DetailNode> forward(DetailNode node) {
84-
return forward(ASTNode.javadoc(node));
85-
}
86-
8783
public static Stream<DetailNode> forward(DetailNode node, Predicate<DetailNode> stopOn) {
8884
var nodes = forward(ASTNode.javadoc(node)).toList();
8985
var result = new ArrayList<DetailNode>();

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/javadoc/JavaDocSupport.java

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -72,26 +72,46 @@ public static String getPackageName(DetailAST node) {
7272
.orElse(List.of()));
7373
}
7474

75+
public static String getQualifiedName(DetailAST node) {
76+
return tree(node.getFirstChild())
77+
.filter(tokens(TokenTypes.DOT).negate())
78+
.map(DetailAST::getText)
79+
.collect(Collectors.joining("."));
80+
}
81+
7582
public static String toQualifiedName(DetailAST classDef, String typeName) {
76-
checkTypeDef(TYPES, classDef);
77-
if (!typeName.contains(".")) {
78-
if (!getSimpleName(classDef).equals(typeName)) {
79-
var cu = getCompilationUnit(classDef);
80-
return children(cu)
81-
.filter(tokens(TokenTypes.IMPORT))
82-
.map(
83-
it ->
84-
tree(it.getFirstChild())
85-
.filter(tokens(TokenTypes.DOT).negate())
86-
.map(DetailAST::getText)
87-
.collect(Collectors.joining(".")))
88-
.filter(qualifiedName -> qualifiedName.endsWith("." + typeName))
89-
.findFirst()
90-
.orElseGet(() -> String.join(".", getPackageName(classDef), typeName));
83+
return switch (typeName) {
84+
case "char", "boolean", "int", "short", "long", "float", "double" -> typeName;
85+
case "Character" -> Character.class.getName();
86+
case "Boolean" -> Boolean.class.getName();
87+
case "Integer" -> Integer.class.getName();
88+
case "Short" -> Short.class.getName();
89+
case "Long" -> Long.class.getName();
90+
case "Float" -> Float.class.getName();
91+
case "Double" -> Double.class.getName();
92+
case "String" -> String.class.getName();
93+
default -> {
94+
checkTypeDef(TYPES, classDef);
95+
if (!typeName.contains(".")) {
96+
if (!getSimpleName(classDef).equals(typeName)) {
97+
var cu = getCompilationUnit(classDef);
98+
yield children(cu)
99+
.filter(tokens(TokenTypes.IMPORT))
100+
.map(
101+
it ->
102+
tree(it.getFirstChild())
103+
.filter(tokens(TokenTypes.DOT).negate())
104+
.map(DetailAST::getText)
105+
.collect(Collectors.joining(".")))
106+
.filter(qualifiedName -> qualifiedName.endsWith("." + typeName))
107+
.findFirst()
108+
.orElseGet(() -> String.join(".", getPackageName(classDef), typeName));
109+
}
110+
}
111+
// Already qualified.
112+
yield typeName;
91113
}
92-
}
93-
// Already qualified.
94-
return typeName;
114+
};
95115
}
96116

97117
private static void checkTypeDef(Predicate<DetailAST> predicate, DetailAST node) {

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/javadoc/MethodDoc.java

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
public class MethodDoc extends JavaDocNode {
2121
private String operationId;
2222
private Map<StatusCode, ResponseExt> throwList;
23+
private List<String> parameterTypes = null;
2324

2425
public MethodDoc(JavaDocParser ctx, DetailAST node, DetailAST javadoc) {
2526
super(ctx, node, javadoc);
@@ -43,11 +44,34 @@ public void setOperationId(String operationId) {
4344
this.operationId = operationId;
4445
}
4546

46-
public List<String> getParameterNames() {
47-
return tree(node)
48-
.filter(tokens(TokenTypes.PARAMETER_DEF))
49-
.map(JavaDocSupport::getSimpleName)
50-
.toList();
47+
public List<String> getParameterTypes() {
48+
if (parameterTypes == null) {
49+
parameterTypes = new ArrayList<>();
50+
var classDef =
51+
backward(node)
52+
.filter(JavaDocSupport.TYPES)
53+
.findFirst()
54+
.orElseThrow(() -> new IllegalArgumentException("Class not found: " + node));
55+
for (var parameter : tree(node).filter(tokens(TokenTypes.PARAMETER_DEF)).toList()) {
56+
var type =
57+
children(parameter)
58+
.filter(tokens(TokenTypes.TYPE))
59+
.findFirst()
60+
.orElseThrow(
61+
() ->
62+
new IllegalArgumentException(
63+
"Parameter type not found: "
64+
+ JavaDocSupport.getSimpleName(parameter)));
65+
parameterTypes.add(
66+
JavaDocSupport.toQualifiedName(classDef, JavaDocSupport.getQualifiedName(type)));
67+
}
68+
}
69+
return parameterTypes;
70+
}
71+
72+
public MethodDoc markAsVirtual() {
73+
parameterTypes = List.of();
74+
return this;
5175
}
5276

5377
public List<String> getJavadocParameterNames() {

modules/jooby-openapi/src/test/java/javadoc/JavaDocParserTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ public void apiDoc() throws Exception {
226226
withMethod(
227227
doc,
228228
"hello",
229-
List.of("name", "age", "list", "str"),
229+
List.of("java.util.List", "int", "java.util.List", "java.lang.String"),
230230
method -> {
231231
assertEquals("This is the Hello /endpoint", method.getSummary());
232232
assertEquals("Operation description", method.getDescription());
@@ -240,7 +240,7 @@ public void apiDoc() throws Exception {
240240
withMethod(
241241
doc,
242242
"search",
243-
List.of("query"),
243+
List.of("javadoc.input.QueryBeanDoc"),
244244
method -> {
245245
assertEquals("Search database.", method.getSummary());
246246
assertEquals("Search DB", method.getDescription());
@@ -253,7 +253,7 @@ public void apiDoc() throws Exception {
253253
withMethod(
254254
doc,
255255
"recordBean",
256-
List.of("query"),
256+
List.of("javadoc.input.RecordBeanDoc"),
257257
method -> {
258258
assertEquals("Record database.", method.getSummary());
259259
assertNull(method.getDescription());
@@ -264,7 +264,7 @@ public void apiDoc() throws Exception {
264264
withMethod(
265265
doc,
266266
"enumParam",
267-
List.of("query"),
267+
List.of("javadoc.input.EnumDoc"),
268268
method -> {
269269
assertEquals("Enum database.", method.getSummary());
270270
assertEquals("Enum doc.", method.getParameterDoc("query"));
@@ -295,7 +295,7 @@ public void noClassDoc() throws Exception {
295295
withMethod(
296296
doc,
297297
"hello",
298-
List.of("name"),
298+
List.of("java.lang.String"),
299299
methodDoc -> {
300300
assertEquals("Method Doc.", methodDoc.getSummary());
301301
assertNull(methodDoc.getDescription());

modules/jooby-openapi/src/test/java/javadoc/PrintAstTree.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import java.io.IOException;
1010
import java.nio.file.Path;
1111
import java.nio.file.Paths;
12-
import javadoc.input.MultilineComment;
12+
import javadoc.input.RecordBeanDoc;
1313

1414
import com.puppycrawl.tools.checkstyle.AstTreeStringPrinter;
1515
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
@@ -25,7 +25,7 @@ public static void main(String[] args) throws CheckstyleException, IOException {
2525
.resolve("java");
2626
var stringAst =
2727
AstTreeStringPrinter.printJavaAndJavadocTree(
28-
baseDir.resolve(toPath(MultilineComment.class)).toFile());
28+
baseDir.resolve(toPath(RecordBeanDoc.class)).toFile());
2929
System.out.println(stringAst);
3030
}
3131

0 commit comments

Comments
 (0)