Я пытаюсь получить доступ к фактическому исходному исходному коду типа из Java-обработчика аннотации. Возможно ли это как-то? Спасибо!
Доступ к исходному коду из Java Annotation Processor
Ответ 1
У меня возникла проблема, когда мне приходилось обращаться к некоторому исходному коду (код инициализатора для константы non-String/non-primitive) и получил его решение, обратившись к исходному коду через 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.
Ответ 2
Mirror API является эквивалентом API Reflection, но во время компиляции. Чтение внутреннего содержимого методов с использованием этого API невозможно. Все остальное должно быть в порядке.
Если вы действительно хотите это сделать, тогда могут быть хаки, чтобы получить входной поток в исходных файлах, которые вы хотите прочитать.
-
Генератор метамодели Hibernate читает XML файлы, используя Filer.getResource(), в XmlParser.getInputStreamForResource(). Проблема в том, что поддерживаются только CLASS_OUTPUT и SOURCE_OUPUT, поэтому они могут вам не подойти.
-
Другое решение включает в себя поиск пути к исходному файлу, а затем просто открытие регулярного потока ввода. Я сделал этот грязный взлом для AndroidAnnotations, чтобы прочитать файл AndroidManifest.xml во время компиляции. См. AndroidManifestFinder.findManifestFile().
Ответ 3
Быстрый ответ заключается в том, что это невозможно.
Из Mirror API JavaDoc, используемого в обработке аннотаций в Sun SDK 5:
Интерфейс Mirror API используется для моделирования семантическая структура программы. Это обеспечивает представления объявленных в программе, таких как классы, методы и поля. Создает ниже уровня метода, таких как отдельные заявления и выражения не представлены.
Java 6 Обработка аннотаций основана на новом API, но по-прежнему не содержит более подробной информации о структуре кода.
Ответ 4
вы можете попробовать API-интерфейс компилятора (http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html)
этот API используется java-компилятором для работы с абстрактным синтаксическим деревом java-программ. это доходит до того, что построены языковые конструкции Java, такие как инструкции, циклы, выражения и т.д.
вы можете найти библиотеку jar в каталоге JDK (named tools.jar)
Ответ 5
Простая адаптация @AdrianoNobre 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();