Я пытаюсь переименовать метод в интерфейсе Java и функцию в интерфейсе Kotlin во время построения в соответствии с переписанием AST (абстрактное синтаксическое дерево). По этому вопросу мы игнорируем последствия, связанные с переименованием метода/функции для вызовов. Чтобы найти метод/функцию для переименования, я использую специальный обработчик аннотации и аннотации. Я работаю над интерфейсом Java, следуя этим инструкциям.
Я создал новый проект с тремя модулями. Модуль приложения, модуль аннотации и модуль обработки аннотаций.
Модуль приложения - приложение для Android и содержит два отдельных файла интерфейса Java и Kotlin с одним аннотированным методом/функцией.
RenameJava.java
package nl.peperzaken.renametest;
import nl.peperzaken.renameannotation.Rename;
public interface RenameJava {
@Rename
void methodToRename();
}
RenameKotlin.kt
package nl.peperzaken.renametest
import nl.peperzaken.renameannotation.Rename
interface RenameKotlin {
@Rename
fun functionToRename()
}
Модуль аннотации представляет собой библиотеку Java, которая содержит @Rename
аннотацию @Rename
и мы указываем ее только на функции, и мы говорим, что она может быть видна только в исходном коде.
Rename.kt
package nl.peperzaken.renameannotation
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class Rename
Модуль обработчика аннотаций представляет собой библиотеку Java, которая содержит только процессор, который выполняет итерацию элементов, содержащих аннотацию, и делает на них преобразования.
RenameProcessor.kt
package nl.peperzaken.renameprocessor
import com.google.auto.service.AutoService
import com.sun.source.util.Trees
import com.sun.tools.javac.processing.JavacProcessingEnvironment
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeTranslator
import com.sun.tools.javac.util.Names
import nl.peperzaken.renameannotation.Rename
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic
@AutoService(Processor::class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("nl.peperzaken.renameannotation.Rename")
class RenameProcessor : AbstractProcessor() {
private lateinit var trees: Trees
private lateinit var names: Names
private val visitor = object : TreeTranslator() {
override fun visitMethodDef(jcMethodDecl: JCTree.JCMethodDecl) {
super.visitMethodDef(jcMethodDecl)
// print original declaration
processingEnv.messager.printMessage(
Diagnostic.Kind.NOTE,
jcMethodDecl.toString()
)
// Rename declaration
jcMethodDecl.name = names.fromString("renamed")
// print renamed declaration
processingEnv.messager.printMessage(
Diagnostic.Kind.NOTE,
jcMethodDecl.toString()
)
// commit changes
result = jcMethodDecl
}
}
@Synchronized
override fun init(processingEnvironment: ProcessingEnvironment) {
super.init(processingEnvironment)
trees = Trees.instance(processingEnvironment)
val context = (processingEnvironment as JavacProcessingEnvironment).context
names = Names.instance(context)
}
override fun process(set: Set<TypeElement>, roundEnvironment: RoundEnvironment): Boolean {
// Find elements that are annotated with @Rename
for (element in roundEnvironment.getElementsAnnotatedWith(Rename::class.java)) {
val tree = trees.getTree(element) as JCTree
tree.accept(visitor)
}
return true
}
}
Грейд файлы
Я добавил в обработчик аннотации build.gradle
:
// Add annotation dependency
implementation project(':rename-annotation')
// Used to generate META-INF so the processor can run
compile 'com.google.auto.service:auto-service:1.0-rc4'
kapt "com.google.auto.service:auto-service:1.0-rc4"
// To prevent unresolved references during building. You might not need this dependency.
implementation files("${System.getProperty('java.home')}/../lib/tools.jar")
Я добавил в приложение build.gradle
:
compileOnly project(':rename-annotation')
annotationProcessor project(':rename-processor')
У аннотации build.gradle
нет зависимостей, кроме генерируемых по умолчанию.
Причина, по которой у нас есть разные модули, заключается в том, что мы можем предотвратить добавление аннотаций и процессора в финальный APK, поскольку нам нужны только те, которые были во время строительства.
Выход
Журнал показывает, что метод в интерфейсе Java был переименован:
Note:
@Rename()
void methodToRename();
Note:
@Rename()
void renamed();
Для интерфейса Kotlin не было журнала. Указание обработчика аннотации не выполнялось.
Когда вы посмотрите на classes.dex
сгенерированного APK, вы увидите следующее:
Вы можете видеть, что метод интерфейса Java был переименован правильно. Хотя функции интерфейса Kotlin нет. Даже если он отображается в журнале.
Вы также заметите это предупреждение в журнале:
app: зависимостей 'annotationProcessor' не будут распознаваться как kapt-аннотационные процессоры. Измените имя конфигурации на "kapt" для этих артефактов: "RenameTest: rename-processor: unspecified" и примените плагин kapt: "применить плагин:" kotlin-kapt "".
Поэтому давайте сделаем то, что предлагает предупреждение. Добавьте apply plugin: 'kotlin-kapt'
в app build.gradle
и измените annotationProcessor
на kapt
. После синхронизации и восстановления выход:
Note:
@Rename()
void methodToRename();
Note:
@Rename()
void renamed();
Note:
@nl.peperzaken.renameannotation.Rename()
public abstract void functionToRename();
Note:
@nl.peperzaken.renameannotation.Rename()
public abstract void renamed();
Появляются журналы для файла Java и Kotlin. Успех, который вы думаете? Если посмотреть на classes.dex
вновь созданного APK, вы подумаете иначе, поскольку оба находятся в их первоначальной форме:
Вопрос
Есть ли способ получить желаемый результат в финальной APK? Поскольку вывод состоит в том, что и метод в интерфейсе Java, и функция в интерфейсе Kotlin переименованы.
Ссылка на образец проекта: https://github.com/peperzaken/kotlin-annotation-rename-test