views:

826

answers:

1

I'm writing a tool that uses the annotation processor to generate source code depending on the return type of methods of an annotated class. The return type is always some subtype (interface or class) of an interface A that defines a type variable T.

interface A<T>{T m();};

I would like to find the type parameter for the method m() return value type variable T.

The return type is represented by the annotation processor as a javax.lang.model.type.TypeMirror instance. The simplest case is to return A directly.

@SomeAnnotation
class SomeClass{
    A<T> x();
}

The processor code to find out T is quite simple. (I'll cast instead of using the visitor API here to keep the code simple.)

DeclaredType type = (DeclaredType) typeMirror;
TypeMirror t =  type.getTypeArguments().get(0);

The TypeMirror of the return type is a javax.lang.model.type.DeclaredType and T is the first type argument. The result t is a javax.lang.model.type.TypeVariable for T. The same works for a concrete return type A<B> (B is some type: interface B{}). The result for t is a DeclaredType representing B.

Things start to get complicated with other result types:

interface Subtype<T> extends A<T>{}
interface Concrete extends A<B>{};
interface Multiple<B,T> extends A<T>{}
interface Bounds<T extends B> extends A<T>{}
interface Hierarchy extends Concrete{}

Subtype<B>          -> DeclaredType B
Subtype<T>          -> TypeVariable T
Concrete            -> DeclaredType B
Multiple<B,T>       -> TypeVariable T or DeclaredType B depeding on Multiple
Multiple<B,B>       -> TypeVariable B
<T extends B> A<T>  -> TypeVariable T with super class bound B
Bound<B>            -> DeclaredType B
Bound<C>      -> DeclaredType C (subtype of B)
Hierarchy           -> TypeVariable T

Is there a way to find the correct type parameter for T without mirroring the whole java type system?

+1  A: 
 public AnnotationProcessor getProcessorFor(
      Set<AnnotationTypeDeclaration> atds,
      AnnotationProcessorEnvironment env) {
     return new SomeAnnotationProcessor(env);
    }

    private static class SomeAnnotationProcessor implements AnnotationProcessor {
     private final AnnotationProcessorEnvironment env;

     SomeAnnotationProcessor(AnnotationProcessorEnvironment env) {
      this.env = env;
     }

     public void process() {
      for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()) {
       System.out.println("in class: " + typeDecl);
       typeDecl.accept(getDeclarationScanner(
         new SomeClassVisitor(), NO_OP));
      }
     }

     private static class SomeClassVisitor extends SimpleDeclarationVisitor {
      @Override
      public void visitMethodDeclaration(
        MethodDeclaration methodDeclaration) {
       System.out.println("visiting method: "+methodDeclaration + " -> "+methodDeclaration.getReturnType());
       methodDeclaration.getReturnType().accept(new SomeTypeVisitor());
      }
     }
    }

    private static class SomeTypeVisitor implements TypeVisitor {

     public void visitClassType(ClassType classType) {   
      System.out.println("classType: " + classType + " -> "+classType.getClass());       
     }

     @Override
     public void visitInterfaceType(InterfaceType interfaceType) {
      Types types = annotationProcessorEnvironment.getTypeUtils();
      TypeDeclaration typeDeclaration = annotationProcessorEnvironment
        .getTypeDeclaration("A");   
      Collection<InterfaceType> superinterfaces = interfaceType
        .getSuperinterfaces();      
      System.out.println("interfaceType: " + interfaceType + " -> "
        + superinterfaces);
      DeclaredType typeOfA = types.getDeclaredType(typeDeclaration);
      boolean isSubTypeOfA = types.isSubtype(interfaceType, typeOfA);   
      if (isSubTypeOfA) {
       findTypeVariable(types, superinterfaces, typeOfA);
      }
      Iterator<TypeMirror> iterator = interfaceType
        .getActualTypeArguments().iterator();
      while (iterator.hasNext()) {
       TypeMirror next = iterator.next();
       next.accept(new SomeTypeVisitor());
      }
     }

     public void visitTypeVariable(TypeVariable typeVariable) {   
      System.out.println("typeVariable: "
        + typeVariable.getDeclaration() + " -> "+typeVariable.getClass());       
     }

     private void findTypeVariable(Types types,
       Collection<InterfaceType> superinterfaces, DeclaredType typeOfA) {
      for (InterfaceType superInterface : superinterfaces) {
       TypeMirror erasure = types.getErasure(superInterface);
       if (erasure.equals(typeOfA)) {
        System.out.println("true, "+superInterface.getActualTypeArguments());
       } else {
        System.out.println("false: " + typeOfA + " =!= "
          + erasure);
        findTypeVariable(types, superInterface.getSuperinterfaces(), typeOfA);
       }
      }
     }


    }
miaubiz
This is to simple. It will return DeclaredType Concrete for @SomeAnnotation class Something{ Concrete x(){} } and interface Concrete extends A<B>. The right result is B.The implementation has to figure out that the type argument for the type variable T in A is B for Concrete. Or simpler put, that the method return value of the method is a subtype of A<B>.The question is: What is the type parameter T in A for a given subtype of A.
Thomas Jung
Your code is basicly the same as: DeclaredType type = (DeclaredType) typeMirror; TypeMirror t = type.getTypeArguments().get(0); from above. The visitor can only differentiate between type variable and declared type.
Thomas Jung
hi, I updated the code. as an aside I get DeclaredType B for Hierarchy, which seems correct.
miaubiz