@@ -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}
0 commit comments