Метаданные
Метаданные - это набор произвольных данных, которые можно привязать к определениям в модели предметной области. Метаданные не считаются основной частью модели, и не взаимодействуют с основным функционалом системы. Они нужны в первую очередь для работы с элементами модели предметной области в стороннем коде, которому требуется хранить и использовать некоторые дополнительные данные, не связанные напрямую с данными модели.
Одним из главных подобных использований является хранение текстовой информации об элементах модели на естественном языке. С учетом частого возникновения подобной необходимости, метаданные поддерживают локализацию.
Пример использования метаданных:
//Предположим, что мы хотим хранить с помощью модели
// представление какого-то графа, состоящего из узлов и связей.
//Тогда, для его графического отображения, нам могут понадобиться доп. данные:
//Класс "Узел"
class GraphNode {
//Отношение "Имеет связь с с другим узлом"
rel isLinkedTo(GraphNode) ;
}
//Первый узел
obj node_1 : GraphNode {
isLinkedTo(node_2);
} [
//Метаданные, привязанные к объекту:
color = "#0000FF"; //Цвет узла, как строка
size = 20; //Визуальный размер узла, как число
RU.text = "Первый узел"; //Локализованные метаданные - текст на русском
EN.text = "First node"; //Локализованные метаданные - текст на английском
]
//Второй узел
obj node_2 : GraphNode [ //Аналогично
color = "#00FF00";
id = 25;
RU.text = "Второй узел";
EN.text = "Second node";
]
В коде метаданные представлены классом MetaData
. Подробнее о нем и его структуре в разделе ниже.
Привязать метаданные можно к любому объекту, реализующему интерфейс MetaOwner
.
Такими являются:
- Большинство определений в модели предметной области
- Классы (
ClassDef
) - Объекты (
ObjectDef
) - Свойства (
PropertyDef
) - Отношения (
RelationshipDef
) - Перечисления (
EnumDef
) - Значения перечислений (
EnumValueDef
)
- Классы (
- Большинство элементов дерева решений (Узлы и некоторые вспомогательные сущности)
Хранение и использование данных
В простом случае метаданные представляют собой простую мапу "ключ-значение", где ключ это некоторая строка название свойства метаданных, а значение может быть произвольного типа (при использовании в коде в метаданные можно положить любые значения).
Положить и получить данные в таких случаях можно соответственно методами MetaData.add(name: String, value: Any)
и MetaData.get(name: String)
.
Однако, с учетом необходимости локализации некоторых значений, ключом по факту является пара строк:
- Код локализации - в типичном использовании строка типа
"RU"
или"EN"
, представляющая используемую локализацию. Может быть равенnull
для нелокализованных метаданных. - Название свойства метаданных. Не может быть равно
null
В данном случае используются методы MetaData.add(locCode: String?, name: String, value: Any)
и MetaData.get(locCode: String?, name: String)
соответственно.
Хранится все это в "двухярусной" мапе:
private val propertyNamesToLocalizations = mutableMapOf<String, MutableMap<String?, Any>>()
Здесь ключом к внешней мапе является как раз название свойства, а к внутренней - код локализации. Таким образом, разные локализации одного и того же свойства хранятся "рядом", в одной внутренней мапе - их все можно получить методом MetaData.getLocalizations()
.