Метаданные

Метаданные - это набор произвольных данных, которые можно привязать к определениям в модели предметной области. Метаданные не считаются основной частью модели, и не взаимодействуют с основным функционалом системы. Они нужны в первую очередь для работы с элементами модели предметной области в стороннем коде, которому требуется хранить и использовать некоторые дополнительные данные, не связанные напрямую с данными модели.
Одним из главных подобных использований является хранение текстовой информации об элементах модели на естественном языке. С учетом частого возникновения подобной необходимости, метаданные поддерживают локализацию.

Пример использования метаданных:

//Предположим, что мы хотим хранить с помощью модели 
// представление какого-то графа, состоящего из узлов и связей.
//Тогда, для его графического отображения, нам могут понадобиться доп. данные: 

//Класс "Узел"
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.
Такими являются:

Хранение и использование данных

В простом случае метаданные представляют собой простую мапу "ключ-значение", где ключ это некоторая строка название свойства метаданных, а значение может быть произвольного типа (при использовании в коде в метаданные можно положить любые значения).
Положить и получить данные в таких случаях можно соответственно методами 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().