Доступ к исходному коду из Java Annotation Processor - PullRequest
16 голосов
/ 16 июня 2011

Я пытаюсь получить доступ к исходному исходному коду типа из обработчика аннотаций Java. Возможно ли это как-то? Спасибо!

Ответы [ 5 ]

20 голосов
/ 20 марта 2012

У меня была проблема, когда мне нужно было получить доступ к некоторому исходному коду (коду инициализатора для не-String / не примитивной константы), и я получил ее, получив доступ к исходному коду через API дерева компиляторов .

Вот общий рецепт:

1. Создайте собственный 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. В вашем AbstractProcessor сохраните ссылку на текущее дерево компиляции, переопределив метод init:

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

3. Получите исходный код инициализации для VariableElement (в вашем случае перечисление):

// 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();

И это все! В конце переменная fieldInitiliazer будет содержать точную строку (и) кода, используемого для инициализации моей константы. С некоторыми изменениями вы сможете использовать тот же рецепт для доступа к исходному коду других типов элементов в исходном дереве (т. Е. Методы, объявления пакетов и т. Д.)

Дополнительную информацию и примеры читайте в этой статье : Анализ исходного кода с использованием API Java 6 .

5 голосов
/ 31 января 2016

Простая адаптация @ AdrianoNobre's answer . Он немного лучше отражает предполагаемое использование шаблона посетителя.

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);
    }
}

Использование сканера для получения тела метода:

MethodScanner methodScanner = new MethodScanner();
MethodTree methodTree = methodScanner.scan(methodElement, this.trees);
methodTree.getBody();
3 голосов
/ 17 июня 2011

Mirror API является эквивалентом API Reflection, но во время компиляции. Чтение внутреннего содержимого методов, использующих этот API, невозможно. Все остальное должно быть в порядке.

Если вы действительно хотите это сделать, то могут быть хаки, чтобы получить поток ввода для исходных файлов, которые вы хотите прочитать.

  • Генератор метамоделей Hibernate считывает файлы XML с помощью Filer.getResource (), в XmlParser.getInputStreamForResource () . Проблема в том, что поддерживаются только CLASS_OUTPUT и SOURCE_OUPUT, поэтому он может не подойти для вас.

  • Другое решение состоит в том, чтобы выяснить путь к исходному файлу, а затем просто открыть обычный поток ввода. Я сделал такой грязный хак для AndroidAnnotations, чтобы прочитать файл AndroidManifest.xml во время компиляции. См. AndroidManifestFinder.findManifestFile () .

2 голосов
/ 16 июня 2011

Быстрый ответ: это невозможно.

Из Зеркального API JavaDoc , используемого при обработке аннотаций в SDK Sun 5:

Зеркальный APIиспользуется для моделирования семантической структуры программы.Он предоставляет представления объектов, объявленных в программе, таких как классы, методы и поля. Конструкции ниже уровня метода, такие как отдельные операторы и выражения, не представлены .

Java 6 Обработка аннотаций основана на newAPI , но он по-прежнему не дает более подробной информации о структуре кода.

1 голос
/ 01 ноября 2011

вы можете попробовать API дерева компиляторов (http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html)
этот API используется компилятором java для работы с абстрактным синтаксическим деревом программ java. Он распространяется на конструкции языка Java, такие как операторы, циклы, выражения и т. Д.. Вы можете найти библиотеку jar в каталоге JDK (с именем tools.jar)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...