# Аннотации

Аннотации позволяют добавлять метаданные к именованным функциям, классам, а также методам/свойствам/конструкторам классов. Эти метаданные затем можно получить и использовать в райнтаме. Каждая аннотация имеет имя, а также ноль или более атрибутов, каждый из которых состоит из имени и значения.

Синтаксис аннотаций:

<атрибут> = <имя-атрибута> '=' <значение-атрибута>
<аннотация> = <имя-аннотации> [ '(' [ <атрибут-1> [ ',' <атрибут-2> ] ... [ ',' ] ] ')' ]
<группа-аннотаций> = ':' '[' <аннотация-1> [ ',' <аннотация-2> ] ... [ ',' ] ']'
<блок-аннотаций> = <группа-аннотаций-1> [ <группа-аннотаций-2> ] ...

Имена атрибутов и аннотаций должны быть идентификаторами. Как и другие идентификаторы они регистронезависимы. Значение атрибута может быть Boolean, Real и String. В процессе анализа исходного кода аннотации всех групп блока объединяются в одну общую группу в порядке их следования, т.е.

':' '[' <аннотация-11> ',' <аннотация-12> ... ']' ... ':' '[' <аннотация-N1> ',' <аннотация-N2> ... ']'

эквивалентно

':' '[' <аннотация-11> <аннотация-12> ... <аннотация-N1> <аннотация-N2> ... ']'

Блоком аннотаций можно помечать именованные функции, классы и методы/свойства/конструкторы классов. Примеры:

# у функции f одна аннотация с именем test без атрибутов

:[test]
func f() {}

# у класса C одна аннотация с именем test и тремя атрибутами:
# - order (Real)
# - description (String)
# - disabled (Boolean)

:[test(order = 7, description = "some test", disabled = true)]
class C {}

# скобки после имени аннотации можно писать, даже если нет атрибутов

:[test()]
func f() {}

# за последним атрибутом может идти запятая

:[test(disabled = true,)]
func f() {}

# группа аннотаций может содержать более одной аннотации

:[test, disabled]
func f() {}

# за последней аннотацией в группе может идти запятая

:[test, disabled,]
func f() {}

# блок аннотаций может содержать несколько групп аннотаций
# все группы блока объединяются в одну общую группу

:[test]
:[disabled]
func f() {}

# эквивалентно

:[test, disabled]
func f() {}

# аннотации можно добавлять к членам класса

class C
{
   :[ctor]
   constructor () { }

   :[getter]
   get x() { return 0 }

   :[setter]
   set x(v) { }

   :[method]
   method() { }
}

# Доступ к аннотациям

Прочитать аннотации в рантайме можно с помощью рефлексии. Каждая аннотированная сущность имеет свойство annotations (аннотации). Это свойство возвращает массив (Array), который содержит все аннотации (экземпляры класса Annotation) сущности в порядке их следования. Возвращаемый массив является копией списка аннотаций, он создается заново каждый раз при обращении к свойству. Поэтому изменение этого массива не изменяет список аннотаций сущности. Сами аннотации также являются неизменяемыми объектами. Имя аннотации можно получить с помощью свойства name (имя), а значения атрибутов - с помощью оператора индексации по имени атрибута. Если сущность не аннотирована, то массив будет пустым. Примеры:

:[test, disabled]
func f() {}

:[test(description = "some test")]
class S
{
   :[setup, lateinit]
   constructor() { }

   :[teardown]
   destroy() { }

   :[getter(for = "x")]
   get x() { return 0 }

   :[setter(for = "y")]
   set y(v) { }
}

f.overloads[1].annotations                      # получение списка аннотаций
                                                # функции f; возвращает массив
                                                # с двумя аннотациями

f.overloads[1].annotations[1].name              # имя первой аннотации "test"
f.overloads[1].annotations[2].name              # имя второй аннотации "disabled"

S.annotations                                   # получение списка аннотаций класса
                                                # C; возвращает массив с одним
                                                # элементом

S.annotations[1].name                           # возвращает имя аннотации "test"
S.annotations[1].description                    # получение значения атрибута
                                                # description; возвращает строку
                                                # "some test"

S.constructor.overloads[1].annotations          # возвращает список аннотаций
                                                # конструктора, т.е. setup и
                                                # lateinit

S.method("destroy").overloads[1].annotations    # список аннотаций метода destroy

S.property("x").getter.annotations              # аннотации геттера свойства x
S.property("y").setter.annotations              # аннотации сеттера свойства y

S.annotations.pushBack("s")                     # не изменяет аннотации S, поскольку
                                                # массив annotations - копия списка
                                                # аннотаций

# Класс Annotation

# Индексация

При индексации именем атрибута возвращается его значение:

:[a(x = 1)]
class S { }

S.annotations[1].x                           # возвращает 1

Изменение атрибутов запрещено:

:[a(x = 1)]
class S { }

S.annotations[1].x = 2                       # ошибка
S.annotations[1].y = 0                       # ошибка
delete S.annotations[1].x                    # ошибка

# Методы

# attributeValue(name: String): Any (значениеАтрибута)

Возвращает значение атрибута аннотации по его имени. Метод можно использовать в том случае, если имя атрибута совпадает с именем свойства или метода самой аннотации (например, name):

:[test(name = 1)]
class S { }

S.annotations[1].attributeValue("name")      # вернет 1

# hasAttribute(name: String): Boolean (естьАтрибут)

Возвращает true, если у аннотации есть атрибут с именем name, и false в противном случае:

:[test(x = 1)]
class S { }

S.annotations[1].hasAttribute("x")           # вернет true
S.annotations[1].hasAttribute("y")           # вернет false

# Свойства

# name: String (имя)

Возвращает имя аннотации:

:[test]
class S { }

S.annotations[1].name                        # вернет "test"

# attributesCount: Real (количествоАтрибутов)

Возвращает количество атрибутов аннотации:

:[test(x = "x", y = "y")]
class S { }

S.annotations[1].attributesCount             # вернет 2

# attributes: Object (атрибуты)

Возвращает объект, ключами которого являются имена атрибутов аннотации, а значениями - значения атрибутов. Этот объект является копией атрибутов аннотации, и создается каждый раз заново при чтении свойства. Поэтому изменение этого объекта не меняет атрибуты самой аннотации.

:[test(x = "a", y = "b")]
class S { }

S.annotations[1].attributes                  # вернет объект @{x: "a", y: "b"}
S.annotations[1].attributes.z = 1            # атрибуты аннотации test не
                                             # меняются, изменилась только
                                             # копия атрибутов, которую
                                             # вернуло свойство attributes