I am writing some tools for our build system to enforce some strict calling conventions on methods belonging to classes containing certain annotations.
I’m using the Compiler Tree API…
What i’m wondering is when traversing the ‘tree’, how can you tell the type of class/interface for a MethodInvocation.
I’m subclassing TreePathScanner with :
@Override
public Object visitMethodInvocation(MethodInvocationTree node, Trees trees) {
}
I’m hoping theres a way to tell the type of the class(or interface) that you’re trying to invoke the method on. Am I going about this the wrong way? Thanks for any ideas…
There are a couple of issues here. You can either be interested in
knowing the Java type of the method invocation receiver or just
knowing the class on the method is invoked. Java information is more
informative as it gives you generic types as well, e.g.
List<String>while Elements would only provide you with the class, e.g.
List<E>.Getting the Element
To get the Element of the class the method is invoked on, you can do
the following:
Corner cases:
1.
invokedClass might be a superclass of the receiver type. So running
the snippet on
new ArrayList<String>.equals(null)would returnAbstractListrather thanArrayList, since equals() is implementedin
AbstractListnotArrayList.2.
When handling array invocations, e.g.
new int[].clone(), you wouldget
TypeElementof classArray.Getting the actual type
To get the type, there is no direct way for determining it what the
receiver type is. There is some complexity in handling method invocations
within inner classes where the receiver is not given explicitly
(e.g. unlike
OuterClass.this.toString()). Here is a sample implementation:Note:
The
receivertype needs to beTypeMirrornotDeclaredTypeunfortunately. When calling
new int[5].clone(),receiverwould bean
ArrayTypeofint[], which is more informative than the previousmethod.
Getting it to run
Both of the previous methods require the compiler to resolve the type
information for the classes. In usual circumstances, the compiler
only resolve the types for method declarations but not the bodies.
Hence, the methods described earlier would return
nullinstead.To have the compiler resolve the type information, you can do one of
the following ways:
1.
Use
AbstractTypeProcessorclass that just got added to thecompiler repository for JDK 7. Check out the work on JSR
308 and their compiler.
While the work is mainly on annotated types, it might be useful for.
The compiler allows you to use the provided class in a backward
compatible manner with Java 5.
This approach allows you to write processors that get invoked just
like your current processors.
2.
Use
JavacTaskinstead and callJavacTask.analyze(). Look atthe main method of this javac test to see how
to invoke your visitor on the classes.
This approach makes your processor look more like an analysis tool
rather than a plug-in to the compiler, as you would need to invoke it
directly rather than have it be a regular process.