处理没有注释的java类型

时间:2016-11-23 22:28:12

标签: java annotation-processing

我有几个使用java注释处理器执行的检查,但我也想检查未注释的类型。

例如,如果我们假设我有一个类似@Responsible(name="xyz")的注释,那么挂钩编译过程以强制所有顶级类型都有注释的最佳方法是什么。

使用我当前的实现,我依赖于两个注释,预期的一个(Responsible)和一个包级别。后者用于“开火”。即使没有预期的注释,注释处理器也是如此。 在解雇的注释处理器内,我然后搜索&过滤磁盘上的java文件(使用传递给编译器的参数)来收集我想要处理的所有文件,并在java文件对应于处理器正在处理的带注释类型时过滤它们。如果有人在未指定注释的情况下提交新文件,则构建失败。

是不是有一种更清洁的方式来查找未注释的'类型?

2 个答案:

答案 0 :(得分:3)

您不必依赖注释来运行处理器。如the documentation中所述:

  

如果不存在注释类型,则仍会出现注释处理,但只有通用处理器支持处理" *"可以声明(空)一组注释类型。

你找到课程的方法有点笨拙。相反,您可以依赖包和类之间的父子关系:找出顶级包元素的名称,包含有趣的类,并使用{{1}下载到该包(和/或它的子包)中}。或者,您可以在该包中找到一个类 - 然后使用Element#getEnclosedElements提升到最顶层的包。可以使用Elements#getPackageElement通过名称获取包对象,并且可以使用Elements#getTypeElement按名称获取类对象。

与手动交互文件和目录相比,不那么脆弱,如果源文件在目录之间移动或拆分,则不会中断。

请注意,包含顺序为Element#getEnclosingElement,每个包的父级都是包本身,而不是它的父级"包"a single package" -> "class" -> "method" -> ...中未包含net.example

答案 1 :(得分:2)

要处理所有元素,我错过了RoundEnvironment#getRootElements()是我正在寻找的元素列表,如果声明处理器使用*处理所有内容。

因此,为了检查所有类型是否都使用@Responsible进行了注释,我最终得到了

@AutoService(Processor.class)
public class ResponsibleAnnotationProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
            List<ElementKind> typeKinds = Arrays.asList(ElementKind.ENUM, ElementKind.INTERFACE, ElementKind.CLASS);
            // let's gather all types we are interrested in
            Set<String> allElements = env.getRootElements()
                    .stream()
                    .filter(e -> typeKinds.contains(e.getKind()))   // keep only interesting elements
                    .map(e -> e.asType().toString())                // get their full name
                    .collect(Collectors.toCollection(() -> new HashSet<>()));
            Set<String> typesWithResponsible = new HashSet<>();

            annotations.forEach(te -> {
                if (Responsible.class.getName().equals(te.asType().toString())) {
                    // We collect elements with an already declared  ownership 
                    env.getElementsAnnotatedWith(te).forEach(e -> typesWithResponsible.add(e.asType().toString()));
                }
            });

            allElements.removeAll(typesWithResponsible);
            allElements.forEach(cname -> processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, cname + " must be annotated with @" + Responsible.class.getName() + " to declare a ownership"));
            return false;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
      return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        // We want to process all elements
        return Collections.singleton("*");
    }
}