How to handle generics inside a Java "annotation processor"? - java

I asked before for an example "annotation processor" that would generate a Proxy/Delegate for an interface, but got no answer, and did not find anything on the Internet, so I made my own.
So far it worked well, until I tried to use generics inside a super-interface. If I use generics in the annotated interface, it works fine (more by accident than by design). But if the annotated interface extends another interface that takes a generic type parameter, that parameter is not "bound" to the type that the annotated interface use when extending the super-interface. Example:
public interface TestFragment<E> {
void test(E dummy);
}
#CreateWrapper
public interface TestService extends TestFragment<String> {
double myOwnMethod();
}
This would generate:
// ...
public void test(final E dummy) {
wrapped.test(dummy);
}
// ...
instead of the correct:
// ...
public void test(final String dummy) {
wrapped.test(dummy);
}
// ...
The code that generates the parameters in the generated methods look like this:
int count = 0;
for (VariableElement param : method.getParameters()) {
if (count > 0) {
pw.print(", ");
}
count++;
pw.printf("final %s %s", param.asType().toString(),
param.getSimpleName().toString());
}
Is there a way to do this?

Have a look at http://docs.oracle.com/javase/6/docs/api/javax/lang/model/util/Types.html#asMemberOf%28javax.lang.model.type.DeclaredType,%20javax.lang.model.element.Element%29
Might be helpful. I used it to solve a very similar problem.

This can be quite simple if you follow Ryan Walls suggestion of using asMemberOf
ExecutableType methodType = (ExecutableType) typeUtil
.asMemberOf((DeclaredType) theAnnotatedClass.asType(), method);
int count = 0;
for (VariableElement param : method.getParameters()) {
if (count > 0) {
pw.print(", ");
}
TypeMirror actualParamType = methodType.getParameterTypes().get(count);
pw.printf("final %s %s", actualParamType.toString(),
param.getSimpleName().toString());
count++;
}

What you need is substitution, given a map of type variables to type arguments. In this case, E->String. Replace any E in any type with String
There is no such support in javax.lang.model.util.Types, you need to roll your own. Basically
void print(TypeMirror type, Map<TypeVariable,TypeMirror> substitution)
if(substitution.containsKey(type)) // type is a var, E
print( substitution.get(type) ); // String
else if(type instanceof DeclaredType) // e.g. List<E>
print( type.asElement().getSimpleName() ); // List
for(TypeMirror arg : type.getTypeArguments() ) // E
print(arg, substitution)
etc. something like that

Copy-paste of my original answer:
This seems to be a common question so, for those arriving from Google: there is hope.
The Dagger DI project is licensed under the Apache 2.0 License and contains some utility methods for working with types in an annotation processor.
In particular, the Util class can be viewed in full on GitHub (Util.java) and defines a method public static String typeToString(TypeMirror type). It uses a TypeVisitor and some recursive calls to build up a string representation of a type. Here is a snippet for reference:
public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator)
{
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
TypeElement typeElement = (TypeElement) declaredType.asElement();
rawTypeToString(result, typeElement, innerClassSeparator);
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result.append("<");
for (int i = 0; i < typeArguments.size(); i++)
{
if (i != 0)
{
result.append(", ");
}
// NOTE: Recursively resolve the types
typeToString(typeArguments.get(i), result, innerClassSeparator);
}
result.append(">");
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v) { ... }
#Override
public Void visitArray(ArrayType arrayType, Void v) { ... }
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
result.append(typeVariable.asElement().getSimpleName());
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v) { ... }
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v) { ... }
}, null);
}
I am busy with my own project which generates class extensions. The Dagger method works for complex situations, including generic inner classes. I have the following results:
My test class with field to extend:
public class AnnotationTest
{
...
public static class A
{
#MyAnnotation
private Set<B<Integer>> _bs;
}
public static class B<T>
{
private T _value;
}
}
Calling the Dagger method on the Element the processor provides for the _bs field:
accessor.type = DaggerUtils.typeToString(element.asType());
The generated source (custom, of course). Note the awesome nested generic types.
public java.util.Set<AnnotationTest.B<java.lang.Integer>> AnnotationTest.A.getBsGenerated()
{
return this._bs;
}
EDIT: adapting the concept to extract a TypeMirror of the first generic argument, null otherwise:
public static TypeMirror getGenericType(final TypeMirror type)
{
final TypeMirror[] result = { null };
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result[0] = typeArguments.get(0);
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v)
{
return null;
}
#Override
public Void visitArray(ArrayType arrayType, Void v)
{
return null;
}
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v)
{
return null;
}
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v)
{
throw new UnsupportedOperationException();
}
}, null);
return result[0];
}

Related

Composition and generics

I'm trying to solve this "composition + generics" situation, and make PostCompany.send(msg) be compatible with the type passed/injected to the class.
What could I change to allow both Fedex and FedexPlus being used as generic Types at PostCompany class, since Fexed's send method expects String as parameter and FeexPlus expects Integer?
interface Poster<T> {
void send(T msg);
}
class Fedex implements Poster<String> {
#Override
public void send(String msg) {
// do something
}
}
class FedexPlus implements Poster<Integer> {
#Override
public void send(Integer msg) {
// do something
}
}
class PostCompany<P extends Poster> {
private final P poster;
public PostCompany(P poster) {
this.poster = poster;
}
public void send(??? msg) { // <-- Here
this.poster.send(msg);
}
}
You missed the type of a Poster
class PostCompany<T, P extends Poster<T>> {
public void send(T msg) { // <-- Here
this.poster.send(msg);
}
}
But it actually better to just type the type of the object
class PostCompany<T> {
private final Poster<T> poster;
public PostCompany(Poster<T> poster) {
this.poster = poster;
}
public void send(T msg) { // <-- Here
this.poster.send(msg);
}
}
Since you will always be using the interface methods of Poster
You are using the raw form of Poster when defining PostCompany. You need to define another type parameter to capture the type argument for Poster.
Then you can use that new type parameter as the type argument to Poster and as the parameter type to the send method.
class PostCompany<T, P extends Poster<T>> {
and
public void send(T msg) {

How could i get Class<? extends List<AlarmRule>> instatnce?

I need to get Class< ? extends List < AlarmRule > > instance.
This is my code:
public static BoundedMatcher<Object, List<AlarmRule>> setBind() {
Class<? extends List<AlarmRule>> clazz = null; // I need to give clazz a value,but i don't know how.
return new BoundedMatcher<Object, List<AlarmRule>>(clazz) {
#Override
public void describeTo(Description description) {
description.appendText("with item content: ");
}
#Override
protected boolean matchesSafely(List<AlarmRule> list) {
return list.stream().anyMatch(alarmRule -> test_reminder_corn.equals(alarmRule.cron));
}
};
}
Thanks for any help!
write like this
public static BoundedMatcher<Object, List> setBind() {
return new BoundedMatcher<Object, List>(List.class) {
#Override
public void describeTo(Description description) {
description.appendText("with item content: ");
}
#Override
protected boolean matchesSafely(List list) {
// convert every object in list to AlarmRule
return false;
}
};
}
In your case you need to pass List.class in the constructor.
However, because of java type erasure, any List may be passed to your matcher so you need to add additional code to ensure your list actually contains AlarmRule objects or you will get a ClassCastException at runtime.

How to implement a generic wrapper for a ResultSet-like API?

I have an third-party RPC-API that provides an interface similar to that of java.sql.ResultSet (for reading values) and java.sql.PreparedStatement (for writing values). Assume it looks something like this:
public interface RemoteDeviceProxy {
public void setBoolean(Boolean value);
public void setInteger(Integer value);
// ...
public Boolean getBoolean();
public Integer getInteger();
// ...
}
I want to write a wrapper for this API that uses generics to create instances of specific types:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
public RemoteVariable(RemoteDeviceProxy wrappedDevice) {
this.wrappedDevice = wrappedDevice;
}
public T get() {
// should call wrappedDevice.getBoolean() if T is Boolean, etc.
// how to implement?
}
public void set(T newValue) {
// should call wrappedDevice.setBoolean(newValue) if T is Boolean, etc.
// implement using instanceof
}
}
How can I implement the getter in my generic wrapper? I have found this answer which explains a similar scenario in depth, but I am not able to transfer this to my problem. Specifically, when I write this:
public T get() {
Type[] actualTypeArguments = ((ParameterizedType) getClass())
.getActualTypeArguments();
}
I get a compiler error saying I cannot cast to ParameterizedType, and I do not understand why. Can anyone explain how to achieve this?
Here is one way:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
private final Class<T> clazz;
public RemoteVariable(RemoteDeviceProxy wrappedDevice, Class<T> clazz) {
this.wrappedDevice = wrappedDevice;
this.clazz = clazz;
}
public T get() {
if(clazz == Boolean.class){return clazz.cast(wrappedDevice.getBoolean());}
else if(clazz == Integer.class){return clazz.cast(wrappedDevice.getInteger());}
// ...
}
// ...
}
I thought over this quite a while and finally came up with a different approach:
First I added a getter to you RemoteVariable class:
protected RemoteDeviceProxy getWrappedProxy() {
return wrappedProxy;
}
Second I created a builder interface that will be used by a factory later:
public interface RemoteVariableBuilder {
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy);
}
Then I created non generic sub classes for Boolean...
public class RemoteBooleanVariable extends RemoteVariable<Boolean> implements RemoteVariableBuilder {
public RemoteBooleanVariable(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteBooleanVariable(wrappedProxy);
}
#Override
public Boolean get() {
return getWrappedProxy().getBoolean();
}
#Override
public void set(Boolean value) {
getWrappedProxy().setBoolean(value);
}
}
... and Integer ...
public class RemoteIntegerBuilder extends RemoteVariable<Integer> implements RemoteVariableBuilder {
public RemoteIntegerBuilder(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteIntegerBuilder(wrappedProxy);
}
#Override
public Integer get() {
return getWrappedProxy().getInteger();
}
#Override
public void set(Integer value) {
getWrappedProxy().setInteger(value);
}
}
actually eclipse created most of the code once it knew base class and interface.
The final step was to create a factory
public class RemoteVariableFactory {
private static final Map<String, RemoteVariableBuilder> BUILDERS = new HashMap<>();
static {
BUILDERS.put(Boolean.class.getName(), new RemoteBooleanVariable(null));
BUILDERS.put(Integer.class.getName(), new RemoteIntegerBuilder(null));
// add more builders here
}
public static <T> RemoteVariable<T> getRemoteVariable(RemoteDeviceProxy wrappedProxy, Class<T> typeClass) {
RemoteVariableBuilder remoteVariableBuilder = BUILDERS.get(typeClass.getName());
if (remoteVariableBuilder == null) {
return null; // or throw an exception whichever is better in your case
}
return remoteVariableBuilder.buildNewVariable(wrappedProxy);
}
}
Now we are ready to create new RemoteVariables...
RemoteVariable<Boolean> var1 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Boolean.class);
RemoteVariable<Integer> var2 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Integer.class);
To conclude this let's do a quick comparison to the answer of Eng.Fouad:
Disadvantage:
you need to create a new class for every datatype you provide
Advantage:
you only have to add one line to the static block of the factory and not two new if blocks to the getter and setter in RemoteVariable
get and set do not have to work through the if-else-blocks every time

How to 'wrap' two classes with identical methods?

I have to handle two classes with identical methods but they don't implement the same interface, nor do they extend the same superclass. I'm not able / not allowed to change this classes and I don't construct instances of this classes I only get objects of this.
What is the best way to avoid lots of code duplication?
One of the class:
package faa;
public class SomethingA {
private String valueOne = null;
private String valueTwo = null;
public String getValueOne() { return valueOne; }
public void setValueOne(String valueOne) { this.valueOne = valueOne; }
public String getValueTwo() { return valueTwo; }
public void setValueTwo(String valueTwo) { this.valueTwo = valueTwo; }
}
And the other...
package foo;
public class SomethingB {
private String valueOne;
private String valueTwo;
public String getValueOne() { return valueOne; }
public void setValueOne(String valueOne) { this.valueOne = valueOne; }
public String getValueTwo() { return valueTwo; }
public void setValueTwo(String valueTwo) { this.valueTwo = valueTwo; }
}
(In reality these classes are larger)
My only idea is now to create a wrapper class in this was:
public class SomethingWrapper {
private SomethingA someA;
private SomethingB someB;
public SomethingWrapper(SomethingA someA) {
//null check..
this.someA = someA;
}
public SomethingWrapper(SomethingB someB) {
//null check..
this.someB = someB;
}
public String getValueOne() {
if (this.someA != null) {
return this.someA.getValueOne();
} else {
return this.someB.getValueOne();
}
}
public void setValueOne(String valueOne) {
if (this.someA != null) {
this.someA.setValueOne(valueOne);
} else {
this.someB.setValueOne(valueOne);
}
}
public String getValueTwo() {
if (this.someA != null) {
return this.someA.getValueTwo();
} else {
return this.someB.getValueTwo();
}
}
public void setValueTwo(String valueTwo) {
if (this.someA != null) {
this.someA.setValueTwo(valueTwo);
} else {
this.someB.setValueTwo(valueTwo);
}
}
}
But I'm not realy satisfied with this solution. Is there any better / more elegant way to solve this problem?
A better solution would be to create an interface to represent the unified interface to both classes, then to write two classes implementing the interface, one that wraps an A, and another that wraps a B:
public interface SomethingWrapper {
public String getValueOne();
public void setValueOne(String valueOne);
public String getValueTwo();
public void setValueTwo(String valueTwo);
};
public class SomethingAWrapper implements SomethingWrapper {
private SomethingA someA;
public SomethingWrapper(SomethingA someA) {
this.someA = someA;
}
public String getValueOne() {
return this.someA.getValueOne();
}
public void setValueOne(String valueOne) {
this.someA.setValueOne(valueOne);
}
public String getValueTwo() {
return this.someA.getValueTwo();
}
public void setValueTwo(String valueTwo) {
this.someA.setValueTwo(valueTwo);
}
};
and then another class just like it for SomethingBWrapper.
There, a duck-typed solution. This will accept any object with valueOne, valueTwo properties and is trivially extensible to further props.
public class Wrapper
{
private final Object wrapped;
private final Map<String, Method> methods = new HashMap<String, Method>();
public Wrapper(Object w) {
wrapped = w;
try {
final Class<?> c = w.getClass();
for (String propName : new String[] { "ValueOne", "ValueTwo" }) {
final String getter = "get" + propName, setter = "set" + propName;
methods.put(getter, c.getMethod(getter));
methods.put(setter, c.getMethod(setter, String.class));
}
} catch (Exception e) { throw new RuntimeException(e); }
}
public String getValueOne() {
try { return (String)methods.get("getValueOne").invoke(wrapped); }
catch (Exception e) { throw new RuntimeException(e); }
}
public void setValueOne(String v) {
try { methods.get("setValueOne").invoke(wrapped, v); }
catch (Exception e) { throw new RuntimeException(e); }
}
public String getValueTwo() {
try { return (String)methods.get("getValueTwo").invoke(wrapped); }
catch (Exception e) { throw new RuntimeException(e); }
}
public void setValueTwo(String v) {
try { methods.get("setValueTwo").invoke(wrapped, v); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
You can use a dynamic proxy to create a "bridge" between an interface you define and the classes that conform but do not implement your interface.
It all starts with an interface:
interface Something {
public String getValueOne();
public void setValueOne(String valueOne);
public String getValueTwo();
public void setValueTwo(String valueTwo);
}
Now you need an InvocationHandler, that will just forward calls to the method that matches the interface method called:
class ForwardInvocationHandler implements InvocationHandler {
private final Object wrapped;
public ForwardInvocationHandler(Object wrapped) {
this.wrapped = wrapped;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Method match = wrapped.getClass().getMethod(method.getName(), method.getParameterTypes());
return match.invoke(wrapped, args);
}
}
Then you can create your proxy (put it in a factory for easier usage):
SomethingA a = new SomethingA();
a.setValueOne("Um");
Something s = (Something)Proxy.newProxyInstance(
Something.class.getClassLoader(),
new Class[] { Something.class },
new ForwardInvocationHandler(a));
System.out.println(s.getValueOne()); // prints: Um
Another option is simpler but requires you to subclass each class and implement the created interface, simply like this:
class SomethingAImpl extends SomethingA implements Something {}
class SomethingBImpl extends SomethingB implements Something {}
(Note: you also need to create any non-default constructors)
Now use the subclasses instead of the superclasses, and refer to them through the interface:
Something o = new SomethingAImpl(); // o can also refer to a SomethingBImpl
o.setValueOne("Uno");
System.out.println(o.getValueOne()); // prints: Uno
i think your original wrapper class is the most viable option...however it can be done using reflection, your real problem is that the application is a mess...and reflection is might not be the method you are looking for
i've another proposal, which might be help: create a wrapper class which has specific functions for every type of classes...it mostly copypaste, but it forces you to use the typed thing as a parameter
class X{
public int asd() {return 0;}
}
class Y{
public int asd() {return 1;}
}
class H{
public int asd(X a){
return a.asd();
}
public int asd(Y a){
return a.asd();
}
}
usage:
System.out.println("asd"+h.asd(x));
System.out.println("asd"+h.asd(y));
i would like to note that an interface can be implemented by the ancestor too, if you are creating these classes - but just can't modify it's source, then you can still overload them from outside:
public interface II{
public int asd();
}
class XI extends X implements II{
}
class YI extends Y implements II{
}
usage:
II a=new XI();
System.out.println("asd"+a.asd());
You probably can exploit a facade along with the reflection - In my opinion it streamlines the way you access the legacy and is scalable too !
class facade{
public static getSomething(Object AorB){
Class c = AorB.getClass();
Method m = c.getMethod("getValueOne");
m.invoke(AorB);
}
...
}
I wrote a class to encapsulate the logging framework API's. Unfortunately, it's too long to put in this box.
The program is part of the project at http://www.github.com/bradleyross/tutorials with the documentation at http://bradleyross.github.io/tutorials. The code for the class bradleyross.library.helpers.ExceptionHelper in the module tutorials-common is at https://github.com/BradleyRoss/tutorials/blob/master/tutorials-common/src/main/java/bradleyross/library/helpers/ExceptionHelper.java.
The idea is that I can have the additional code that I want to make the exception statements more useful and I won't have to repeat them for each logging framework. The wrapper isn't where you eliminate code duplication. The elimination of code duplication is in not having to write multiple versions of the code that calls the wrapper and the underlying classes. See https://bradleyaross.wordpress.com/2016/05/05/java-logging-frameworks/
The class bradleyross.helpers.GenericPrinter is another wrapper that enables you to write code that works with both the PrintStream, PrintWriter, and StringWriter classes and interfaces.

Find type parameter of method return type in Java 6 annotation processor

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<T> 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?
Have a look at http://docs.oracle.com/javase/6/docs/api/javax/lang/model/util/Types.html#asMemberOf%28javax.lang.model.type.DeclaredType,%20javax.lang.model.element.Element%29
I used it to solve this problem and contributed the solution to the WsDoc project in this pull request: https://github.com/versly/wsdoc/pull/7
I did something like this:
Type.MethodType methodType = (Type.MethodType) processingEnv.getTypeUtils().asMemberOf(declaredTypeThatExtendsSomeGenericParent, methodToGetReturnTypeForAsExecutableElement);
TypeMirror type = methodType.getReturnType();
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);
}
}
}
}
This seems to be a common question so, for those arriving from Google: there is hope.
The Dagger DI project is licensed under the Apache 2.0 License and contains some utility methods for working with types in an annotation processor.
In particular, the Util class can be viewed in full on GitHub (Util.java) and defines a method public static String typeToString(TypeMirror type). It uses a TypeVisitor and some recursive calls to build up a string representation of a type. Here is a snippet for reference:
public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator)
{
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
TypeElement typeElement = (TypeElement) declaredType.asElement();
rawTypeToString(result, typeElement, innerClassSeparator);
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result.append("<");
for (int i = 0; i < typeArguments.size(); i++)
{
if (i != 0)
{
result.append(", ");
}
// NOTE: Recursively resolve the types
typeToString(typeArguments.get(i), result, innerClassSeparator);
}
result.append(">");
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v) { ... }
#Override
public Void visitArray(ArrayType arrayType, Void v) { ... }
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
result.append(typeVariable.asElement().getSimpleName());
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v) { ... }
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v) { ... }
}, null);
}
I am busy with my own project which generates class extensions. The Dagger method works for complex situations, including generic inner classes. I have the following results:
My test class with field to extend:
public class AnnotationTest
{
...
public static class A
{
#MyAnnotation
private Set<B<Integer>> _bs;
}
public static class B<T>
{
private T _value;
}
}
Calling the Dagger method on the Element the processor provides for the _bs field:
accessor.type = DaggerUtils.typeToString(element.asType());
The generated source (custom, of course). Note the awesome nested generic types.
public java.util.Set<AnnotationTest.B<java.lang.Integer>> AnnotationTest.A.getBsGenerated()
{
return this._bs;
}
EDIT: adapting the concept to extract a TypeMirror of the first generic argument, null otherwise:
public static TypeMirror getGenericType(final TypeMirror type)
{
final TypeMirror[] result = { null };
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result[0] = typeArguments.get(0);
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v)
{
return null;
}
#Override
public Void visitArray(ArrayType arrayType, Void v)
{
return null;
}
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v)
{
return null;
}
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v)
{
throw new UnsupportedOperationException();
}
}, null);
return result[0];
}

Categories

Resources