Skip to content

Commit df7590a

Browse files
committed
build: cleanup after huge refactor
- get back tests passed
1 parent 81ad12c commit df7590a

File tree

13 files changed

+440
-134
lines changed

13 files changed

+440
-134
lines changed

modules/jooby-apt/src/main/java/io/jooby/internal/apt/McpRouter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static McpRouter parse(MvcContext context, TypeElement controller) {
3939

4040
@Override
4141
public String getGeneratedType() {
42-
return context.generateRouterName(getTargetType().getQualifiedName().toString() + "Mcp");
42+
return context.generateRouterName(getTargetType().getQualifiedName() + "Mcp");
4343
}
4444

4545
public String getMcpServerKey() {

modules/jooby-apt/src/main/java/io/jooby/internal/apt/RestRoute.java

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private String methodReference(boolean kt, String thisRef, String methodName) {
6666

6767
private Optional<String> dispatch() {
6868
var dispatch = dispatch(method);
69-
return dispatch.isEmpty() ? dispatch(method.getEnclosingElement()) : dispatch;
69+
return dispatch.isEmpty() ? dispatch(router.getTargetType()) : dispatch;
7070
}
7171

7272
private Optional<String> dispatch(Element element) {
@@ -79,7 +79,7 @@ private Optional<String> dispatch(Element element) {
7979
}
8080

8181
private Optional<String> mediaType(Function<Element, List<String>> lookup) {
82-
var scopes = List.of(method, method.getEnclosingElement());
82+
var scopes = List.of(method, router.getTargetType());
8383
var i = 0;
8484
var types = Collections.<String>emptyList();
8585
while (types.isEmpty() && i < scopes.size()) {
@@ -119,8 +119,7 @@ public List<String> generateMapping(boolean kt, String routerName, boolean isLas
119119
var httpMethod =
120120
HttpMethod.findByAnnotationName(httpMethodAnnotation.getQualifiedName().toString());
121121
var dslMethod = httpMethodAnnotation.getSimpleName().toString().toLowerCase();
122-
var paths =
123-
context.path((TypeElement) method.getEnclosingElement(), method, httpMethodAnnotation);
122+
var paths = context.path(router.getTargetType(), method, httpMethodAnnotation);
124123
var targetMethod = methodName;
125124

126125
var thisRef =
@@ -192,14 +191,26 @@ public List<String> generateHandlerCall(boolean kt) {
192191
var buffer = new ArrayList<String>();
193192
var methodName = getGeneratedName();
194193
var paramList = new StringJoiner(", ", "(", ")");
195-
var returnTypeGenerics =
196-
getReturnType().getArgumentsString(kt, false, Set.of(TypeKind.TYPEVAR));
197-
var returnTypeString = type(kt, getReturnType().toString());
194+
198195
var customReturnType = getReturnType();
196+
var returnTypeGenerics =
197+
customReturnType.getArgumentsString(kt, false, Set.of(TypeKind.TYPEVAR));
198+
var returnTypeString = type(kt, customReturnType.toString());
199+
200+
String projection = getProjection();
201+
202+
// Bulletproof check: Is the controller natively returning a Projected type?
203+
boolean isProjectedReturnType =
204+
customReturnType.isProjection() || customReturnType.is(Types.PROJECTED);
199205

200-
if (customReturnType.isProjection()) {
201-
returnTypeGenerics = "";
202-
returnTypeString = Types.PROJECTED + "<" + returnType + ">";
206+
// 1. Create separate variables for the generated HTTP handler's signature
207+
String handlerTypeGenerics = returnTypeGenerics;
208+
String handlerTypeString = returnTypeString;
209+
210+
// 2. ONLY modify the signature if we need to wrap a NON-projected type
211+
if (projection != null && !isProjectedReturnType) {
212+
handlerTypeGenerics = "";
213+
handlerTypeString = Types.PROJECTED + "<" + returnTypeString + ">";
203214
}
204215

205216
boolean nullable =
@@ -208,8 +219,8 @@ public List<String> generateHandlerCall(boolean kt) {
208219
"ctx",
209220
methodName,
210221
buffer,
211-
returnTypeGenerics,
212-
returnTypeString,
222+
handlerTypeGenerics,
223+
handlerTypeString,
213224
!method.getThrownTypes().isEmpty());
214225

215226
int controllerIndent = 2;
@@ -260,14 +271,12 @@ public List<String> generateHandlerCall(boolean kt) {
260271
buffer.add(statement(indent(controllerIndent), "return statusCode", semicolon(kt)));
261272
} else {
262273
var castStr =
263-
customReturnType.isProjection()
274+
isProjectedReturnType
264275
? ""
265276
: customReturnType.getArgumentsString(kt, false, Set.of(TypeKind.TYPEVAR));
266-
var needsCast =
267-
!castStr.isEmpty()
268-
|| (kt
269-
&& !customReturnType.isProjection()
270-
&& !customReturnType.getArguments().isEmpty());
277+
278+
// Force cast only if the return type contains Type Variables (like <E>)
279+
var needsCast = !castStr.isEmpty();
271280
var kotlinNotEnoughTypeInformation = !castStr.isEmpty() && kt ? "<Any>" : "";
272281

273282
var call =
@@ -279,12 +288,13 @@ public List<String> generateHandlerCall(boolean kt) {
279288

280289
if (needsCast) {
281290
setUncheckedCast(true);
291+
// Use the RAW return type string for the cast, not the modified handler type
282292
call = kt ? call + " as " + returnTypeString : "(" + returnTypeString + ") " + call;
283293
}
284294

285-
if (customReturnType.isProjection()) {
286-
var projected =
287-
of(Types.PROJECTED, ".wrap(", call, ").include(", string(getProjection()), ")");
295+
// 3. ONLY wrap the call if it's a NON-projected type with a projection string
296+
if (projection != null && !isProjectedReturnType) {
297+
var projected = of(Types.PROJECTED, ".wrap(", call, ").include(", string(projection), ")");
288298
buffer.add(
289299
statement(
290300
indent(controllerIndent),
@@ -376,8 +386,16 @@ public String getProjection() {
376386
.findFirst()
377387
.orElse(null);
378388
}
379-
var httpMethod = httpMethodAnnotation.getAnnotationMirrors().getFirst();
380-
var projection = AnnotationSupport.findAnnotationValue(httpMethod, "projection"::equals);
381-
return projection.stream().findFirst().orElse(null);
389+
390+
// Get the annotation mirror from the method, not the annotation type definition
391+
var httpMethodMirror =
392+
AnnotationSupport.findAnnotationByName(
393+
method, httpMethodAnnotation.getQualifiedName().toString());
394+
if (httpMethodMirror != null) {
395+
var projection =
396+
AnnotationSupport.findAnnotationValue(httpMethodMirror, "projection"::equals);
397+
return projection.stream().findFirst().orElse(null);
398+
}
399+
return null;
382400
}
383401
}

modules/jooby-apt/src/main/java/io/jooby/internal/apt/RestRouter.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,32 @@ public RestRouter(MvcContext context, TypeElement clazz) {
2323
public static RestRouter parse(MvcContext context, TypeElement controller) {
2424
RestRouter router = new RestRouter(context, controller);
2525

26-
for (var enclosed : controller.getEnclosedElements()) {
27-
if (enclosed.getKind() == ElementKind.METHOD) {
28-
ExecutableElement method = (ExecutableElement) enclosed;
29-
30-
if (AnnotationSupport.findAnnotationByName(method, "io.jooby.annotation.JsonRpc") != null
31-
|| AnnotationSupport.findAnnotationByName(method, "io.jooby.annotation.Trpc") != null) {
32-
continue;
33-
}
26+
for (TypeElement type : context.superTypes(controller)) {
27+
for (var enclosed : type.getEnclosedElements()) {
28+
if (enclosed.getKind() == ElementKind.METHOD) {
29+
ExecutableElement method = (ExecutableElement) enclosed;
30+
31+
// Ignore abstract methods
32+
if (method.getModifiers().contains(javax.lang.model.element.Modifier.ABSTRACT)) {
33+
continue;
34+
}
3435

35-
for (var annoMirror : method.getAnnotationMirrors()) {
36-
TypeElement annoElement = (TypeElement) annoMirror.getAnnotationType().asElement();
37-
if (HttpMethod.hasAnnotation(annoElement)) {
38-
RestRoute route = new RestRoute(router, method, annoElement);
39-
router.routes.put(route.getMethodName() + annoElement.getSimpleName(), route);
36+
for (var annoMirror : method.getAnnotationMirrors()) {
37+
TypeElement annoElement = (TypeElement) annoMirror.getAnnotationType().asElement();
38+
String annoName = annoElement.getQualifiedName().toString();
39+
40+
// Explicitly ignore RPC annotations so they don't generate invalid REST routes
41+
if (annoName.startsWith("io.jooby.annotation.Trpc")
42+
|| annoName.equals("io.jooby.annotation.JsonRpc")
43+
|| annoName.startsWith("io.jooby.annotation.Mcp")) {
44+
continue;
45+
}
46+
47+
if (HttpMethod.hasAnnotation(annoElement)) {
48+
RestRoute route = new RestRoute(router, method, annoElement);
49+
String uniqueKey = method.toString() + annoElement.getSimpleName();
50+
router.routes.putIfAbsent(uniqueKey, route);
51+
}
4052
}
4153
}
4254
}
@@ -46,7 +58,9 @@ public static RestRouter parse(MvcContext context, TypeElement controller) {
4658
var grouped =
4759
router.routes.values().stream().collect(Collectors.groupingBy(RestRoute::getMethodName));
4860
for (var overloads : grouped.values()) {
49-
if (overloads.size() > 1) {
61+
long distinctMethods =
62+
overloads.stream().map(r -> r.getMethod().toString()).distinct().count();
63+
if (distinctMethods > 1) {
5064
for (var route : overloads) {
5165
var paramsString =
5266
route.getRawParameterTypes(true, false).stream()
@@ -133,7 +147,9 @@ public String getSourceCode(Boolean generateKotlin) throws IOException {
133147
.append(System.lineSeparator())
134148
.append(System.lineSeparator());
135149

150+
var generatedHandlers = new java.util.HashSet<>();
136151
getRoutes().stream()
152+
.filter(it -> generatedHandlers.add(it.getGeneratedName()))
137153
.flatMap(it -> it.generateHandlerCall(kt).stream())
138154
.forEach(line -> buffer.append(CodeBlock.indent(4)).append(line));
139155

0 commit comments

Comments
 (0)