Java – How do I get full return type forwarding in Xtend Active Annotations?

How do I get full return type forwarding in Xtend Active Annotations?… here is a solution to the problem.

How do I get full return type forwarding in Xtend Active Annotations?

I’m trying out Xtend’s Active Annotations by writing a simple “logged”
The annotation used for tracing when the method is called. Basically I want to write this in Xtend :

@Logged
override onCreate()
{
   sampleFuncCall()
}

And get something like this in Java:

@Override void onCreate()
{
   Log.d("TAG", "onCreate started");
   sampleFuncCall();
   Log.d("TAG", "onCreate ended");
}

This is my first attempt :

@Active(LoggedAnnotationProcessor)
@Target(ElementType.METHOD)
annotation Logged {
}

class LoggedAnnotationProcessor extends AbstractMethodProcessor
{
    override doTransform(MutableMethodDeclaration method, extension TransformationContext context) 
    {
        val prefix = "wrapped_"
        method.declaringType.addMethod(prefix + method.simpleName) [ m |
            m.static = method.static
            m.visibility = Visibility.PRIVATE
            m.docComment = method.docComment            
            m.exceptions = method.exceptions
            method.parameters.forEach[ p | m.addParameter(p.simpleName, p.type) ]
            m.body = method.body
            m.primarySourceElement = method
            m.returnType = method.returnType
        ]
        val voidMethod = method.returnType === null || method.returnType.void
        method.body = [ ''' 
                try {
                    android.util.Log.d("TAG", "«method.simpleName» start");
                    «IF (!voidMethod) method.returnType» ret = «ENDIF»
                    «prefix + method.simpleName»(«method.parameters.map[simpleName].join(", ")»);
                    android.util.Log.d("TAG", "«method.simpleName» end");
                    «IF (!voidMethod)»return ret; «ENDIF»
                }
                catch(RuntimeException e) {
                    android.util.Log.d("TAG", "«method.simpleName» ended with exception " 
                        + e.getClass().getSimpleName() + "\n" + e.getMessage());
                    throw e;                
                }
            ''']
    }
}

(Note that I can’t find a way to modify method.body, and I have to create a new private method with the “wrapped_” prefix.) I’m guessing this is bad for performance, so if you know how to modify the body of the method directly, please share).

I’m having trouble handling methods that return void.
If the return type of the method is not explicitly declared, I get the following error:

Cannot call method ‘isVoid’ on a inferred type reference before the
compilation phase. Check isInferred() before calling any methods.

Ok, I can add a check for method.returnType.inferred, but how to handle it – it seems like at this stage we still don’t know if it’s going to be invalid, but know the return value for the forwarding method.

Please tell me what is the correct way to write such a note, thank you!

Solution

Maybe you should postpone the calculation. to the end

method.body = [
    val voidMethod = method.returnType === null || method.returnType.void

''' 
            try {
                android.util.Log.d("TAG", "«method.simpleName» start");
                «IF (!voidMethod)»«method.returnType» ret = «ENDIF»
                «prefix + method.simpleName»(«method.parameters.map[simpleName].join(", ")»);
                android.util.Log.d("TAG", "«method.simpleName» end");
                «IF (!voidMethod)»return ret; «ENDIF»
            }
            catch(RuntimeException e) {
                android.util.Log.d("TAG", "«method.simpleName» ended with exception " 
                    + e.getClass().getSimpleName() + "\n" + e.getMessage());
                throw e;                
            }
        ''']

Related Problems and Solutions