Java注释处理API访问import语句

Den*_*søe 7 java annotation-processing

我正在编写一个AnnotationProcessor,它应该生成java代码.它应该从某些现有接口生成派生接口.

为此,我需要找到原始输入代码的import语句,以便我可以在生成的java文件中输出它.

如何才能做到这一点?

kap*_*pex 8

您无法使用注释处理器获取import 语句.你可以得到的是该类使用的类型,甚至更好.

源代码中的Import语句不足以分析类中使用的类型,因为并非所有使用的类型都具有import语句.如果您确实只需要实际语句,则可以直接读取源文件.

如果您只查看语句,则存在一些问题:

  • 完全限定的类名,例如属性 java.util.Date date;
  • 从同一个包导入没有明确的import语句
  • 为文件中的所有类声明imports语句
  • 未使用的import语句可能会导致更多混淆

使用注释处理器和Mirror API,您可以获得属性类型,方法参数,方法返回类型等 - 基本上是不在方法或块中的每个声明的类型.这应该足够好了.

您应该分析类的每个元素并将其类型存储在Set中.有一些实用程序类可以帮助完成此任务.您可以忽略java.lang包中的任何类型,因为它始终是隐式导入的.

最小注释处理器可能如下所示:

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("*")
public class Processor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        ImportScanner scanner = new ImportScanner();
        scanner.scan(roundEnv.getRootElements(), null);

        Set<String> importedTypes = scanner.getImportedTypes();
        // do something with the types

        return false;
    }

}
Run Code Online (Sandbox Code Playgroud)

这里的扫描仪扩展了ElementScanner7基于访客模式.我们只实现一些访问者方法并按类型过滤元素,因为并非所有元素都可以实际包含可导入类型.

import java.util.HashSet;
import java.util.Set;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.util.ElementScanner7;

public class ImportScanner extends ElementScanner7<Void, Void> {

    private Set<String> types = new HashSet<>();

    public Set<String> getImportedTypes() {
        return types;
    }

    @Override
    public Void visitType(TypeElement e, Void p) {
        for(TypeMirror interfaceType : e.getInterfaces()) {
            types.add(interfaceType.toString());
        }
        types.add(e.getSuperclass().toString());
        return super.visitType(e, p);
    }

    @Override
    public Void visitExecutable(ExecutableElement e, Void p) {
        if(e.getReturnType().getKind() == TypeKind.DECLARED) {
            types.add(e.getReturnType().toString());
        }
        return super.visitExecutable(e, p);
    }

    @Override
    public Void visitTypeParameter(TypeParameterElement e, Void p) {
        if(e.asType().getKind() == TypeKind.DECLARED) {
            types.add(e.asType().toString());
        }
        return super.visitTypeParameter(e, p);
    }

    @Override
    public Void visitVariable(VariableElement e, Void p) {
        if(e.asType().getKind() == TypeKind.DECLARED) {
            types.add(e.asType().toString());
        }
        return super.visitVariable(e, p);
    }

}
Run Code Online (Sandbox Code Playgroud)

此扫描程序将一组类型作为完全限定的路径返回.还有一些事情需要考虑,有些事情要实施:

  • 该集合包含java.lang来自同一包的元素和类型
  • 该集包含泛型,如 java.util.List<String>
  • TypeKind.DECLARED并不是唯一一种可导入类型的元素.还要检查TypeKind.ARRAY并获取它的实际声明类型.可以使用而不是else if(e.asType().getKind() == TypeKind.ARRAY) // ...在类中添加另一个TypeKindVisitor7
  • 根据用例,可能会发现更多类型.例如,注释可以包含类作为参数.
  • 对于Java 1.6使用相应的ElementScanner6,TypeKindVisitor6等实现.