处理 java 种无注释的类型

process java types without annotation

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

例如,如果我们假设我有一个像 @Responsible(name="xyz") 这样的注解,那么挂接到编译过程以强制所有顶级类型都存在该注解的最佳方法是什么。

在我当前的实现中,我依赖于两个注释,预期的注释(负责)和包级别的注释。后者用于 'fire' 注释处理器,即使不存在预期的注释也是如此。 在触发的注释处理器中,我然后搜索并过滤磁盘上的 java 文件(使用传递给编译器的参数)以收集我想要处理的所有文件并在 java 文件对应时过滤它们处理器正在处理的注释类型。如果有人在没有指定注释的情况下提交新文件,则构建失败。

没有更简洁的方法来查找 'non annotated' 类型吗?

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

If there are no annotation types present, annotation processing still occurs but only universal processors which support processing "*" can claim the (empty) set of annotation types.

您查找 classes 的方法有点笨拙。相反,您可以依赖包和 classes 之间的父子关系:找出包含有趣的 classes 的顶级包元素的名称,然后进入该包(and/or它是子包)使用 Element#getEnclosedElements。或者,您可以在该包内找到一个 class — 然后使用 Element#getEnclosingElement 提升到最顶层的包。可以使用 Elements#getPackageElement and class objects can be obtained by name using Elements#getTypeElement.

按名称获取包对象

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

注意,包含顺序是 "a single package" -> "class" -> "method" -> ...,每个包的父级都是包本身,而不是 "parent" 包(net.example 不包含在 net 中).

为了处理所有元素,我错过了 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("*");
    }
}