diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 9385df54..754d21da 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -6,11 +6,14 @@ #include "Exceptions/TargetDescriptionParsingFailureException.hpp" #include "src/Logger/Logger.hpp" #include "src/Services/PathService.hpp" +#include "src/Services/StringService.hpp" namespace Targets::TargetDescription { using namespace Exceptions; + using Services::StringService; + const std::map& TargetDescriptionFile::mapping() { return GeneratedMapping::map; } @@ -42,6 +45,31 @@ namespace Targets::TargetDescription return familyIt->second; } + std::optional> TargetDescriptionFile::tryGetPropertyGroup( + std::string_view keyStr + ) const { + auto keys = StringService::split(keyStr, '.'); + + const auto firstSubGroupIt = this->propertyGroupsMappedByKey.find(*keys.begin()); + return firstSubGroupIt != this->propertyGroupsMappedByKey.end() + ? keys.size() > 1 + ? firstSubGroupIt->second.getSubGroup(keys | std::ranges::views::drop(1)) + : std::optional(std::cref(firstSubGroupIt->second)) + : std::nullopt; + } + + const PropertyGroup& TargetDescriptionFile::getPropertyGroup(std::string_view keyStr) const { + const auto propertyGroup = this->tryGetPropertyGroup(keyStr); + + if (!propertyGroup.has_value()) { + throw Exception( + "Failed to get property group \"" + std::string(keyStr) + "\" from TDF - property group not found" + ); + } + + return propertyGroup->get(); + } + void TargetDescriptionFile::init(const std::string& xmlFilePath) { auto file = QFile(QString::fromStdString(xmlFilePath)); if (!file.exists()) { @@ -60,12 +88,12 @@ namespace Targets::TargetDescription } void TargetDescriptionFile::init(const QDomDocument& document) { - const auto device = document.elementsByTagName("device").item(0).toElement(); - if (!device.isElement()) { - throw TargetDescriptionParsingFailureException("Device element not found."); + const auto deviceElement = document.documentElement(); + if (deviceElement.nodeName() != "device") { + throw TargetDescriptionParsingFailureException("Root \"device\" element not found."); } - const auto deviceAttributes = device.attributes(); + const auto deviceAttributes = deviceElement.attributes(); for (auto i = 0; i < deviceAttributes.length(); ++i) { const auto deviceAttribute = deviceAttributes.item(i); this->deviceAttributesByName.insert( @@ -76,8 +104,18 @@ namespace Targets::TargetDescription ); } + for ( + auto element = deviceElement.firstChildElement("property-groups").firstChildElement("property-group"); + !element.isNull(); + element = element.nextSiblingElement("property-group") + ) { + auto propertyGroup = TargetDescriptionFile::propertyGroupFromXml(element); + this->propertyGroupsMappedByKey.insert( + std::pair(propertyGroup.key, std::move(propertyGroup)) + ); + } + this->loadAddressSpaces(document); - this->loadPropertyGroups(document); this->loadModules(document); this->loadPeripheralModules(document); this->loadVariants(document); @@ -85,6 +123,59 @@ namespace Targets::TargetDescription this->loadInterfaces(document); } + std::optional TargetDescriptionFile::tryGetAttribute( + const QDomElement& element, + const QString& attributeName + ) { + return element.hasAttribute(attributeName) + ? std::optional(element.attribute(attributeName).toStdString()) + : std::nullopt; + } + + std::string TargetDescriptionFile::getAttribute(const QDomElement& element, const QString& attributeName) { + const auto attribute = TargetDescriptionFile::tryGetAttribute(element, attributeName); + + if (!attribute.has_value()) { + throw Exception( + "Failed to fetch attribute from TDF element \"" + element.nodeName().toStdString() + + "\" - attribute \"" + attributeName.toStdString() + "\" not found" + ); + } + + return *attribute; + } + + PropertyGroup TargetDescriptionFile::propertyGroupFromXml(const QDomElement& xmlElement) { + auto output = PropertyGroup(TargetDescriptionFile::getAttribute(xmlElement, "key"), {}, {}); + + for ( + auto element = xmlElement.firstChildElement("property"); + !element.isNull(); + element = element.nextSiblingElement("property") + ) { + auto property = TargetDescriptionFile::propertyFromXml(element); + output.propertiesMappedByKey.insert(std::pair(property.key, std::move(property))); + } + + for ( + auto element = xmlElement.firstChildElement("property-group"); + !element.isNull(); + element = element.nextSiblingElement("property-group") + ) { + auto subGroup = TargetDescriptionFile::propertyGroupFromXml(element); + output.subGroupsMappedByKey.insert(std::pair(subGroup.key, std::move(subGroup))); + } + + return output; + } + + Property TargetDescriptionFile::propertyFromXml(const QDomElement& xmlElement) { + return Property( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "value") + ); + } + AddressSpace TargetDescriptionFile::addressSpaceFromXml(const QDomElement& xmlElement) { if ( !xmlElement.hasAttribute("id") @@ -346,38 +437,6 @@ namespace Targets::TargetDescription } } - void TargetDescriptionFile::loadPropertyGroups(const QDomDocument& document) { - const auto deviceElement = document.elementsByTagName("device").item(0).toElement(); - - auto propertyGroupNodes = deviceElement.elementsByTagName("property-groups").item(0).toElement() - .elementsByTagName("property-group"); - - for (int propertyGroupIndex = 0; propertyGroupIndex < propertyGroupNodes.count(); propertyGroupIndex++) { - auto propertyGroupElement = propertyGroupNodes.item(propertyGroupIndex).toElement(); - auto propertyGroupName = propertyGroupElement.attributes().namedItem( - "name" - ).nodeValue().toLower().toStdString(); - PropertyGroup propertyGroup; - propertyGroup.name = propertyGroupName; - - auto propertyNodes = propertyGroupElement.elementsByTagName("property"); - for (int propertyIndex = 0; propertyIndex < propertyNodes.count(); propertyIndex++) { - auto propertyElement = propertyNodes.item(propertyIndex).toElement(); - auto propertyName = propertyElement.attributes().namedItem("name").nodeValue(); - - Property property; - property.name = propertyName.toStdString(); - property.value = propertyElement.attributes().namedItem("value").nodeValue(); - - propertyGroup.propertiesMappedByName.insert( - std::pair(propertyName.toLower().toStdString(), property) - ); - } - - this->propertyGroupsMappedByName.insert(std::pair(propertyGroup.name, propertyGroup)); - } - } - void TargetDescriptionFile::loadModules(const QDomDocument& document) { const auto deviceElement = document.elementsByTagName("device").item(0).toElement(); diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/TargetDescription/TargetDescriptionFile.hpp index 8982abef..02576853 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.hpp @@ -2,6 +2,12 @@ #include #include +#include +#include +#include +#include +#include +#include #include "AddressSpace.hpp" #include "MemorySegment.hpp" @@ -82,10 +88,14 @@ namespace Targets::TargetDescription */ [[nodiscard]] TargetFamily getFamily() const; + [[nodiscard]] std::optional> tryGetPropertyGroup( + std::string_view keyStr + ) const; + [[nodiscard]] const PropertyGroup& getPropertyGroup(std::string_view keyStr) const; protected: std::map deviceAttributesByName; std::map addressSpacesMappedById; - std::map propertyGroupsMappedByName; + std::map> propertyGroupsMappedByKey; std::map modulesMappedByName; std::map peripheralModulesMappedByName; std::map> peripheralRegisterGroupsMappedByModuleRegisterGroupName; @@ -105,6 +115,12 @@ namespace Targets::TargetDescription void init(const std::string& xmlFilePath); void init(const QDomDocument& document); + static std::optional tryGetAttribute(const QDomElement& element, const QString& attributeName); + static std::string getAttribute(const QDomElement& element, const QString& attributeName); + + static PropertyGroup propertyGroupFromXml(const QDomElement& xmlElement); + static Property propertyFromXml(const QDomElement& xmlElement); + /** * Constructs an AddressSpace object from an XML element. * @@ -158,11 +174,6 @@ namespace Targets::TargetDescription */ void loadAddressSpaces(const QDomDocument& document); - /** - * Extracts all property groups and loads them into this->propertyGroupsMappedByName. - */ - void loadPropertyGroups(const QDomDocument& document); - /** * Extracts all modules and loads them into this->modulesMappedByName. */