开发者

Accessing source code from Java Annotation Processor

开发者 https://www.devze.com 2023-03-13 05:52 出处:网络
I am trying to access the actual original source code of a type from 开发者_如何转开发within a Java Annotation Processor. Is this possible somehow? Thanks!I had a problem where I had to access some so

I am trying to access the actual original source code of a type from 开发者_如何转开发within a Java Annotation Processor. Is this possible somehow? Thanks!


I had a problem where I had to access some source code (the initializer code for a non-String/non-primitive constant) and got it solved by accessing the source code via the Compiler Tree API.

Here's the general recipe:

1. Create a custom TreePathScanner:

private static class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees> {

private String fieldName;

private String fieldInitializer;

public void setFieldName(String fieldName) {
    this.fieldName = fieldName;
}

public String getFieldInitializer() {
    return this.fieldInitializer;
}

@Override
public Object visitVariable(VariableTree variableTree, Trees trees) {
    if (variableTree.getName().toString().equals(this.fieldName)) {
        this.fieldInitializer = variableTree.getInitializer().toString();
    }

    return super.visitVariable(variableTree, trees);
}

2. In your AbstractProcessor, save a reference to the current compilation tree by overriding the init method:

@Override
public void init(ProcessingEnvironment pe) {
    super.init(pe);
    this.trees = Trees.instance(pe);
}

3. Get the initialization source code for the VariableElement (in your case an enum):

// assuming theClass is a javax.lang.model.element.Element reference
// assuming theField is a javax.lang.model.element.VariableElement reference
String fieldName = theField.getSimpleName().toString();
CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner();
TreePath tp = this.trees.getPath(theClass);

codeScanner.setFieldName(fieldName);
codeScanner.scan(tp, this.trees);
String fieldInitializer = codeScanner.getFieldInitializer();

And that's it! In the end the fieldInitiliazer variable is going to contain the exact line(s) of code used to initialize my constant. With some tweaking you should be able to use the same recipe to access the source code of other element types in the source tree (i.e. methods, package declarations, etc)

For more reading and examples read this article: Source Code Analysis Using Java 6 APIs.


Simple adaptation of @AdrianoNobre's answer. It reflects the intended use of visitor pattern bit better.

AbstractProcessor init:

private Trees trees;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    trees = Trees.instance(processingEnv);
}

MethodScanner

private static class MethodScanner extends TreePathScanner<List<MethodTree>, Trees> {
    private List<MethodTree> methodTrees = new ArrayList<>();

    public MethodTree scan(ExecutableElement methodElement, Trees trees) {
        assert methodElement.getKind() == ElementKind.METHOD;

        List<MethodTree> methodTrees = this.scan(trees.getPath(methodElement), trees);
        assert methodTrees.size() == 1;

        return methodTrees.get(0);
    }

    @Override
    public List<MethodTree> scan(TreePath treePath, Trees trees) {
        super.scan(treePath, trees);
        return this.methodTrees;
    }

    @Override
    public List<MethodTree> visitMethod(MethodTree methodTree, Trees trees) {
        this.methodTrees.add(methodTree);
        return super.visitMethod(methodTree, trees);
    }
}

Using the scanner to get method body:

MethodScanner methodScanner = new MethodScanner();
MethodTree methodTree = methodScanner.scan(methodElement, this.trees);
methodTree.getBody();


Quick answer is that it's not possible.

From the Mirror API JavaDoc used in Annotation Processing in Sun's SDK 5:

The Mirror API is used to model the semantic structure of a program. It provides representations of the entities declared in a program, such as classes, methods, and fields. Constructs below the method level, such as individual statements and expressions, are not represented.

Java 6 Annotation Processing is based on a new API, but it still doesn't provide any more detail about the code structure.


you may try Compiler Tree API (http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html)
this API is used by java compiler to work with abstract syntax tree of java programs. it goes so far as Java language constructs such as statements, loops, expressions, etc. you can find jar library in your JDK directory (named tools.jar)


The Mirror API is an equivalent of the Reflection API, but at compile time. Reading the internal content of methods using this API is not possible. Anything else should be OK.

If you really want to do this, then there might be hacks to get an input stream on the source files you want to read.

  • Hibernate Metamodel Generator reads XML files using Filer.getResource(), in XmlParser.getInputStreamForResource(). The problem is that only CLASS_OUTPUT and SOURCE_OUPUT are supported, so it might not be suitable for you.

  • The other solution involves finding out the path of the source file, and then just open a regular input stream. I have done this kind of dirty hack for AndroidAnnotations, to read the AndroidManifest.xml file at compile time. See AndroidManifestFinder.findManifestFile().

0

精彩评论

暂无评论...
验证码 换一张
取 消