With Java7 and Java8, I would like to generate a warning if some methods was called. The warning will be print if a specific jar is present when then user compile.
I
As an alternative to the great answer from @emory, you can consider using the pluggable type-checking annotation processing provided by the Checker Framework. The advantage is it can help you to easily determinate the type of the method invoker. Here is an example processor based on the checker framework (add checker.jar to the classpath when compile).
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyTypeProcessor extends AbstractTypeProcessor {
class MyTreePathScanner extends TreePathScanner<Void, Void> {
private final Trees trees;
private final TreePath root;
public MyTreePathScanner(TreePath root) {
this.trees = Trees.instance(processingEnv);
this.root = root;
}
@Override
public Void visitMemberSelect(MemberSelectTree node, Void aVoid) {
ExpressionTree expression = node.getExpression();
TreePath expr = TreePath.getPath(root, expression);
TypeMirror type = trees.getTypeMirror(expr);
Element typeElement = processingEnv.getTypeUtils().asElement(type);
Optional<? extends Element> invoker = typeElement.getEnclosedElements().stream().filter(
e -> e.getSimpleName().equals(node.getIdentifier())).findFirst();
if (invoker.isPresent() && invoker.get().getKind() == ElementKind.METHOD) {
System.out.println("Type: " + typeElement + ", method: " + invoker.get());
}
return super.visitMemberSelect(node, aVoid);
}
}
@Override
public void typeProcess(TypeElement typeElement, TreePath root) {
new MyTreePathScanner(root).scan(root, null);
}
}
Which is processing the following input source.
public class Test {
public void foo() {
}
public static void main(String[] args) {
System.out.println("Hello world!");
Test t = new Test();
t.foo();
}
}
Here is the output:
Type: java.io.PrintStream, method: println()
Type: Test, method: foo()
You can do something like:
package mystuff;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;
@SupportedAnnotationTypes("*")
public class Proc extends AbstractProcessor{
@Override
public boolean process(Set<?extends TypeElement>annotations,RoundEnvironment roundEnvironment){
final Trees trees=Trees.instance(processingEnv);
for(Element element:roundEnvironment.getRootElements()){
TreePath path=trees.getPath(element);
final CompilationUnitTree compilationUnit=path.getCompilationUnit();
compilationUnit.accept(new TreeScanner<Object,Object>(){
@Override
public Object visitMethodInvocation(MethodInvocationTree tree,Object data){
tree.getMethodSelect().accept(new SimpleTreeVisitor<Object,Object>(){
@Override
public Object visitMemberSelect(MemberSelectTree tree,Object data){
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,String.format("class: %1$s\nmethod: %2$s",tree.getExpression(),tree.getIdentifier()));
return null;
}
},null);
return null;
}
},null);
}
return true;
}
}
I used that processor to process the below class
package stuff;
import java.util.*;
@MyAnnotation
class MyProgram{
public void run(){
System.out.println("Hello World!");
}
}
and achieved this result:
class: System.out
method: println
I am pretty sure that the method name generated is what you are looking for. I am pretty sure that the "class" is not exactly what you are looking for, but is a pretty good start.
In my example you probably wanted it to print "java.io.PrintStream" for the class. To get that you could use processingEnv.getElementUtils().getTypeElement("java.lang.System")
to get a TypeElement representing the system class. Then you can use processingEnv.getElementUtils().getAllMembers()
to get every single member of the system class. Iterate through that to find out
. Use the asType
method to get its type.
The preceding paragraph was a gross simplification. The processor did not know a priori that out
is a static member of a class that is part of the implicitly imported java.lang
package. So your code will have to try and fail to find the following classes System
and java.util.System
(because it is in the imports), System.out
, java.util.System.out
, and java.lang.System.out
.
I only dealt with MemberSelect. You will have to deal with other possibilities including MethodInvocation. For example new Object().toString().hashCode()
should be class=Object, method=hashCode.