обрабатывать типы Java без аннотации

У меня есть несколько проверок, которые выполняются с использованием процессоров аннотаций Java, но я также хотел бы выполнить проверки для типов, которые не аннотированы.

Например, если мы предположим, что у меня есть аннотация типа @Responsible(name="xyz"), как лучше всего подключиться к процессу компиляции, чтобы обеспечить наличие аннотации для всех типов верхнего уровня.

В моей текущей реализации я полагаюсь на две аннотации, ожидаемую (Ответственную) и одну на уровне пакета. Последний используется для «запуска» обработчика аннотаций, даже если ожидаемой аннотации нет. Затем внутри запущенного обработчика аннотаций я ищу и фильтрую java-файлы на диске (используя переданные компилятору аргументы), чтобы собрать все файлы, которые я хотел бы обработать, и отфильтровать их, когда java-файл соответствует аннотированному типу, который обрабатывает процессор. . Таким образом, если кто-то фиксирует новый файл без указания аннотации, сборка завершается ошибкой.

Нет более чистого способа найти «неаннотированные» типы?


person Matthieu BROUILLARD    schedule 23.11.2016    source источник


Ответы (2)


Вам не нужно полагаться на аннотации, чтобы ваш процессор работал. Как объясняется в документации:

Если типы аннотаций отсутствуют, обработка аннотаций все равно происходит, но только универсальные процессоры, поддерживающие обработку «*», могут требовать (пустой) набор типов аннотаций.

Ваш подход к поиску классов немного неуклюжий. Вместо этого вы можете полагаться на родительско-дочерние отношения между пакетами и классами: узнайте имя элемента пакета верхнего уровня, который содержит интересные классы, и спуститесь в этот пакет (и / или его подпакеты), используя Element#getEnclosedElements. В качестве альтернативы вы можете найти один класс внутри этого пакета, а затем перейти к самому верхнему пакету, используя Element#getEnclosingElement. Объект пакета можно получить по имени с помощью Элементы # getPackageElement и объекты классов можно получить по имени с помощью Elements # getTypeElement.

Гораздо менее хрупкий по сравнению с ручным взаимодействием с файлами и каталогами и не сломается, если исходные файлы перемещаются или разделяются между каталогами.

Обратите внимание, что порядок включения - "a single package" -> "class" -> "method" -> ..., родительским элементом каждого пакета является сам пакет, а не его «родительский» пакет (net.example не содержится в net).

person user1643723    schedule 24.11.2016
comment
проблема в том, что Element#getEnclosedElements не позволяет обходить субпакеты, я буду много исследовать с * процессором и с roundEnv.getRootElements() - person Matthieu BROUILLARD; 25.11.2016
comment
@MatthieuBROUILLARD Да, это именно то, что я имел в виду выше. К счастью, ничто не мешает вам использовать String#split для самостоятельного получения подпакетов :) - person user1643723; 25.11.2016
comment
спасибо за ваш намек на использование * это был правильный путь. - person Matthieu BROUILLARD; 25.11.2016

Чтобы обработать все элементы, я пропустил, что 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("*");
    }
}
person Matthieu BROUILLARD    schedule 25.11.2016