Kotlin Аннотация: Получить внутренние аннотированные свойства интерфейса - PullRequest
1 голос
/ 18 марта 2020

Обзор

Я пытаюсь собрать свой первый процессор аннотаций, и он идет довольно хорошо. Я создаю процессор генерации кода, который в основном генерирует SharedPreferences для определенного интерфейса. Мои текущие аннотации SharedPrefs и Default. @SharedPrefs информирует процессор о том, что файл является интерфейсом и нуждается в сгенерированном файле prefs. @Default - это то, что я аннотировал некоторые свойства в интерфейсе, чтобы дать процессору понять, как установить значение по умолчанию. Может быть несколько файлов, определенных как @SharedPrefs.

Реализация

В настоящее время я использую следующий код для получения списка файлов, помеченных @SharedPrefs и @Default s:

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
  ...
  roundEnv.getElementsAnnotatedWith(Default::class.java)?.forEach {
    ...
  }
}

@SharedPrefs:

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class SharedPrefs(val showTraces: Boolean = false)

@Default:

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.PROPERTY)
annotation class Default(val defaultValue: String = "[null]")

Используется:

@SharedPrefs
interface InstanceSettings {
    var wifiPassword: String
    @Default("20")
    var count: Int
}

Проблема

Как есть, внутренний forEach возвращает все свойства из всех файлов, помеченных @Default. Генерация кода работает нормально, но это не лучший способ продвижения вперед. Есть ли способ получить только свойства в текущем @SharedPrefs классе, который я обрабатываю? Например, что-то вроде:

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
  ...
  element.methodToGetAnnotatedProperties(Default::class.java)?.forEach {
    ...
  }
}

* РЕДАКТИРОВАТЬ *

Я обнаружил, что для методов, которые я аннотирую

@SomeAnnotation
fun someMethod()

я могу l oop через element.enclosingElements и найдите аннотацию, используя enclosingElement.getAnnotation(<SomeAnnotation::class.java>). К сожалению, и исправьте меня, если я ошибаюсь, я не могу аннотировать интерфейсные переменные с аннотацией AnnotationTarget.FIELD, поскольку у них нет поля поддержки, поскольку это интерфейс, и они не реализованы. Поэтому я использую AnnotationTarget.PROPERTY. При зацикливании вмещающих элементов все переменные отображаются как геттеры и сеттеры. Для приведенного выше примера для InstanceSettings я получаю getWifiPassword, setWifiPassword, getCount и setCount. Я не получаю элемент всего за wifiPassword или count. Вызов getAnnotation(Default::class.java) всегда вернет null для них, поскольку они генерируются.

Кроме того, любые другие ресурсы по обработке аннотаций, о которых кто-либо знает, будут отличным дополнением в комментариях. Спасибо!

Ответы [ 2 ]

0 голосов
/ 19 марта 2020

Так что я думаю, что нашел решение:

Для примера InterfaceSettings:

@SharedPrefs
interface InstanceSettings {
    var wifiPassword: String
    @Default("20")
    var count: Int
}

Упрощенный сгенерированный Java код:

public interface InstanceSettings {
   @NotNull
   String getWifiPassword();

   void setWifiPassword(@NotNull String var1);

   int getCount();

   void setCount(int var1);

   public static final class DefaultImpls {
      public static void count$annotations() {
      }
   }
}

Элемент, возвращаемый при вызове roundEnv.getElementsAnnotatedWith(Default::class.java), равен count$annotations. Поэтому, когда я звоню getEnclosingElement(), DefaultImpls возвращается. Если я позвоню getEnclosingElement() снова , InstanceSettings возвращается. Я на самом деле вызываю getSimpleName() для этого результата и сравниваю его с className из элемента, помеченного @SharedPrefs, чтобы узнать, является ли он дочерним:

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
  val className = element.simpleName.toString()
  val defaults = roundEnv.getElementsAnnotatedWith(Default::class.java)?.filter {
    // it.enclosingElement -> DefaultImpls
    // DefaultImpls.enclosingElement -> com.mypackage.InstanceSettings
    it.enclosingElement.enclosingElement.simpleName == className
  }
}
0 голосов
/ 18 марта 2020

Подобный вопрос задавался java здесь

Вот как вы можете создать функцию расширения, которая будет выполнять за вас работу!

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
    ...
    roundEnv.findNestedElements(SharedPrefs::class, Default::class)?.forEach {
        ...
    }
}

fun <T> RoundEnvironment.findNestedElements(parent: KClass<*>, child: KClass<T>): List<Element>? {
    val childs = this.getElementsAnnotatedWith(child.java)
    val list = ArrayList<Element>()
    for (element in childs)
    {
        if (element.getEnclosingElement().getAnnotation(parent.java) != null)
        {
            list.add(element)
        }
    }
    return if(list.isEmpty()) null else list
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...