void QrsMetamodelLoader::parseObjectsOnDiagram(const qrRepo::RepoApi &repo, Metamodel &metamodel, const Id &diagram)
{
	if (diagram.isNull() || diagram == Id::rootId()) {
		return;
	}

	for (const Id &id : repo.children(diagram)) {
		if (!repo.isLogicalElement(id)) {
			continue;
		}

		const Id type = id.type();
		if (type == metamodelEnumType) {
			parseEnum(repo, metamodel, id);
		} else if (type == metamodelPortType) {
			parsePort(repo, metamodel, id);
		} else if (type == metamodelGroupType) {
			parseGroup(repo, metamodel, diagram, id);
		} else if (type == metamodelImportType) {
			parseImport(repo, metamodel, id);
		} else if (type == metamodelNodeType) {
			parseNode(repo, metamodel, diagram, id);
		} else if (type == metamodelEdgeType) {
			parseEdge(repo, metamodel, diagram, id);
		}
	}
}
QList<QPair<QString, QString>> QrsMetamodelLoader::parseEnumValues(const qrRepo::RepoApi &repo, const Id &id)
{
	QList<QPair<QString, QString>> result;
	for (const Id &child : repo.children(id)) {
		if (child.type() == metamodelEnumValueType && repo.isLogicalElement(child)) {
			result << qMakePair(validateName(repo, child), stringProperty(repo, child, "displayedName"));
		}
	}

	return result;
}
void QrsMetamodelLoader::parseProperties(const qrRepo::RepoApi &repo, ElementType &element, const Id &id)
{
	const IdList children = repo.children(id);

	for (const Id &child : children) {
		if (child.type() == metamodelAttributeType && repo.isLogicalElement(id)) {
			const QString type = repo.stringProperty(child, "attributeType");
			element.addProperty(repo.name(child), type
					, stringProperty(repo, child, "defaultValue", "string")
					, stringProperty(repo, child, "displayedName")
					, stringProperty(repo, child, "description")
					, type == "reference");
		}
	}
}
QString QrsMetamodelLoader::validateRootNode(const qrRepo::RepoApi &repo, const Id &diagram)
{
	if (!repo.hasProperty(diagram, "nodeName")) {
		return QString();
	}

	const QString rootNode = repo.property(diagram, "nodeName").toString();
	for (const Id &child : repo.children(diagram)) {
		if (repo.name(child) == rootNode && (child.type() == metamodelNodeType || child.type() == metamodelGroupType)) {
			return rootNode;
		}
	}

	emit errorOccured(QObject::tr("Root node for diagram %1 (which is %2) does not exist!")
			.arg(repo.name(diagram)).arg(rootNode), diagram);
	return rootNode;
}
void QrsMetamodelLoader::parseContainerProperties(const qrRepo::RepoApi &repo, NodeElementType &node, const Id &id)
{
	const IdList elements = repo.children(id);

	for (const Id &child : elements) {
		if (child.type() == metamodelPropertiesAsContainerType && repo.isLogicalElement(child)) {
			node.setContainer(true);
			node.setSizeOfForestalling(intVectorProperty(repo, child, "forestallingSize", {0,0,0,0}));
			node.setSizeOfChildrenForestalling(intProperty(repo, child, "childrenForestallingSize"));

			node.setSortingContainer(boolProperty(repo, child, "sortContainer"));
			node.setChildrenMovable(!boolProperty(repo, child, "banChildrenMove"));
			node.setMinimizesToChildren(boolProperty(repo, child, "minimizeToChildren"));
			node.setMaximizesChildren(boolProperty(repo, child, "maximizeChildren"));
		}
	}
}
void QrsMetamodelLoader::parseGroupNodes(const qrRepo::RepoApi &repo, QDomElement &parent, const Id &id)
{
	/// @todo: We should not use XML here, PatternType must not parse XML at all.
	for (const Id &child : repo.children(id)) {
		if (repo.isLogicalElement(child)) {
			QDomElement groupNodeTag = parent.ownerDocument().createElement("groupNode");
			groupNodeTag.setAttribute("name", validateName(repo, child));
			groupNodeTag.setAttribute("parent", stringProperty(repo, child, "parent"));
			groupNodeTag.setAttribute("xPosition", stringProperty(repo, child, "xPosition"));
			groupNodeTag.setAttribute("yPosition", stringProperty(repo, child, "yPosition"));

			const Id typeElement = Id::loadFromString(stringProperty(repo, child, "type"));
			groupNodeTag.setAttribute("type", validateName(repo, typeElement));

			parent.appendChild(groupNodeTag);
		}
	}
}
void QrsMetamodelLoader::parseLinksOnDiagram(const qrRepo::RepoApi &repo, Metamodel &metamodel, const Id &diagram)
{
	QHash<QPair<ElementType *, ElementType *>, QString> overridingProperties;
	QSet<ElementType *> elements;
	const QString diagramName = validateName(repo, diagram);

	for (const Id &id : repo.children(diagram)) {
		const Id elementType = id.type();
		if ((elementType == metamodelNodeType || elementType == metamodelEdgeType
				|| elementType == metamodelImportType) && repo.isLogicalElement(id))
		{
			const IdList inLinks = repo.incomingLinks(id);
			for (const Id &inLink : inLinks) {
				if (!repo.isLogicalElement(inLink)) {
					continue;
				}

				if (inLink.type() == metamodelInheritanceLinkType) {
					ElementType *from = nullptr;
					ElementType *to = nullptr;
					QString overridingProperty;
					parseGeneralization(repo, metamodel, inLink, diagramName, from, to, overridingProperty);
					overridingProperties[qMakePair(from, to)] = overridingProperty;
					elements.insert(from);
					elements.insert(to);
				} else if (inLink.type() == metamodelContainmentLinkType) {
					parseContainer(repo, metamodel, inLink, diagramName);
				} else if (inLink.type() == metamodelExplosionLinkType) {
					parseExplosion(repo, metamodel, inLink, diagramName);
				}
			}
		}
	}

	resolveInheritance(elements, overridingProperties);
}