29#include <unordered_set>
30#include <unordered_map>
36#include "musx/util/Logger.h"
37#include "musx/xml/XmlInterface.h"
38#include "musx/dom/BaseClasses.h"
39#include "musx/dom/Document.h"
59 using std::runtime_error::runtime_error;
94 if (resolvers.count(std::string(key)) > 0) {
98 resolvers[std::string(key)] = std::move(resolver);
111 for (
const auto& [key, resolver] : resolvers) {
125 std::map<std::string, Resolver> resolvers;
145 template<
typename DataType,
typename ParserFunc>
146 static void getFieldFromXml(
const XmlElementPtr& element,
const std::string& nodeName, DataType& dataField, ParserFunc parserFunc,
bool expected =
false)
148 if (
auto childElement = element->getFirstChildElement(nodeName)) {
149 dataField = parserFunc(childElement);
150 }
else if (expected) {
151 std::stringstream msg;
152 msg <<
"Expected field <" << element->getTagName() <<
"><" << nodeName <<
"> not found.";
163 auto childElement = element->getFirstChildElement(childElementName);
165 throw std::invalid_argument(
"Missing <" + childElementName +
"> element.");
173 auto childElement = element->getFirstChildElement(childElementName);
177 return childElement->getText();
184 auto childElement = element->getFirstChildElement(childElementName);
188 return childElement->getTextAs<T>(defaultValue);
192 virtual ~FactoryBase() {}
195#ifndef DOXYGEN_SHOULD_IGNORE_THIS
197template <
typename EnumClass,
typename FromClass = std::
string_view>
198using XmlEnumMappingElement = std::unordered_map<FromClass, EnumClass>;
199template <
typename EnumClass,
typename FromClass = std::
string_view>
202 static const XmlEnumMappingElement<EnumClass, FromClass> mapping;
205#define MUSX_XML_ENUM_MAPPING(Type, ...) \
207const XmlEnumMappingElement<Type> XmlEnumMapping<Type>::mapping = __VA_ARGS__
209template <
typename EnumClass,
bool IgnoreUnknown,
typename FromClass = std::
string_view>
215 static EnumClass xmlToEnum(
const FromClass& value)
217 auto it = XmlEnumMapping<EnumClass>::mapping.find(value);
218 if (it != XmlEnumMapping<EnumClass>::mapping.end()) {
221 if constexpr (!IgnoreUnknown) {
222 std::string msg = [value]() {
223 if constexpr (std::is_arithmetic_v<FromClass>) {
224 return "Invalid enum value from xml: " + std::to_string(value);
227 return "Invalid enum value from xml: " + std::string(value);
230 MUSX_UNKNOWN_XML(msg);
236template<
typename EnumClass,
typename FromClass,
bool IgnoreUnknown = false>
237EnumClass toEnum(
const FromClass& value)
239 if constexpr (std::is_convertible_v<FromClass, std::string_view>) {
240 return EnumMapper<EnumClass, IgnoreUnknown, std::string_view>::xmlToEnum(value);
242 return EnumMapper<EnumClass, IgnoreUnknown, FromClass>::xmlToEnum(value);
246template<
typename EnumClass,
bool IgnoreUnknown = false>
247EnumClass toEnum(const ::musx::xml::XmlElementPtr& e)
249 return toEnum<EnumClass, std::string_view, IgnoreUnknown>(e->getTextTrimmed());
252#define MUSX_XML_ELEMENT_ARRAY(Type, ...) \
253const ::musx::xml::XmlElementArray<Type>& Type::xmlMappingArray() { \
254 static const ::musx::xml::XmlElementArray<Type> instance = __VA_ARGS__; \
257static_assert(true, "")
259using ResolverEntry = std::optional<ElementLinker::Resolver>;
261struct ResolverContainer
263 inline static const ResolverEntry resolver = {};
266#define MUSX_RESOLVER_ENTRY(Type, ...) \
268struct ResolverContainer<Type> { \
269 inline static const ResolverEntry resolver = ElementLinker::Resolver(__VA_ARGS__); \
273struct FieldPopulator :
public FactoryBase
275 static void populateField(
const std::shared_ptr<T>& instance,
const XmlElementPtr& fieldElement)
277 auto it = elementXref().find(fieldElement->getTagName());
278 if (it != elementXref().end()) {
279 std::get<1>(*it)(fieldElement, instance);
281 const bool requireFields = [instance]() {
282 if constexpr (std::is_base_of_v<Base, T>) {
283 return instance->requireAllFields();
289 MUSX_UNKNOWN_XML(
"xml element <" + fieldElement->getParent()->getTagName() +
"> has child <" + fieldElement->getTagName() +
"> which is not in the element list.");
294 static void populate(
const std::shared_ptr<T>& instance,
const XmlElementPtr& element)
296 if constexpr (std::is_base_of_v<TextsBase, T>) {
297 instance->text = element->getText();
299 for (
auto child = element->getFirstChildElement(); child; child = child->getNextSibling()) {
300 populateField(instance, child);
303 if constexpr (std::is_base_of_v<Base, T>) {
304 instance->integrityCheck();
308 template <
typename SubClass = T>
309 static void populate(
const std::shared_ptr<T>& instance,
const XmlElementPtr& element, ElementLinker& elementLinker)
311 populate(instance, element);
312 if (ResolverContainer<SubClass>::resolver.has_value()) {
313 elementLinker.addResolver(ResolverContainer<SubClass>::resolver.value(), element->getTagName());
317 template <
typename... Args>
318 static std::shared_ptr<T> createAndPopulate(
const XmlElementPtr& element, Args&&... args)
320 return FieldPopulator<T>::createAndPopulateImpl(element, std::forward<Args>(args)...);
324 static const std::unordered_map<std::string_view, XmlElementPopulator<T>>& elementXref()
326 static const std::unordered_map<std::string_view, XmlElementPopulator<T>> xref = []()
328 std::unordered_map<std::string_view, XmlElementPopulator<T>> retval;
329 auto mappingArray = T::xmlMappingArray();
330 for (std::size_t i = 0; i < mappingArray.size(); i++) {
332 retval[std::get<0>(descriptor)] = std::get<1>(descriptor);
339 template <
typename... Args>
340 static std::shared_ptr<T> createAndPopulateImpl(
const XmlElementPtr& element, Args&&... args)
342 auto instance = std::make_shared<T>(std::forward<Args>(args)...);
343 FieldPopulator<T>::populate(instance, element);
353template <
typename EnumClass,
typename EmbeddedClass>
354static void populateEmbeddedClass(
const XmlElementPtr& e, std::unordered_map<EnumClass, std::shared_ptr<EmbeddedClass>>& listArray)
356 auto typeAttr = e->findAttribute(
"type");
358 throw std::invalid_argument(
"<" + e->getTagName() +
"> element has no type attribute");
360 listArray.emplace(toEnum<EnumClass>(typeAttr->getValueTrimmed()), FieldPopulator<EmbeddedClass>::createAndPopulate(e));
369static std::vector<T> populateEmbeddedArray(
const XmlElementPtr& e,
const std::string_view& elementNodeName)
371 std::vector<T> result;
372 for (
auto child = e->getFirstChildElement(); child; child = child->getNextSibling()) {
373 if (child->getTagName() != elementNodeName) {
374 MUSX_UNKNOWN_XML(
"Unknown tag <" + child->getTagName() +
"> while processing embedded xml array <" + e->getTagName() +
">");
377 if constexpr (std::is_fundamental_v<T>) {
378 result.push_back(child->getTextAs<T>());
379 }
else if constexpr (std::is_same_v<T, std::string>) {
380 result.push_back(child->getText());
382 result.push_back(FieldPopulator<T>::createAndPopulate(child));
389template <
typename... Args>
390inline std::shared_ptr<FontInfo> FieldPopulator<FontInfo>::createAndPopulate(
const XmlElementPtr& element, Args&&... args)
392 if (!element->getFirstChildElement())
return nullptr;
393 return FieldPopulator<FontInfo>::createAndPopulateImpl(element, std::forward<Args>(args)...);
396void populateFontEfx(
const XmlElementPtr& e,
const std::shared_ptr<dom::FontInfo>& i);
399static bool populateBoolean(
const XmlElementPtr& element,
const std::shared_ptr<T>& instance)
401 MUSX_ASSERT_IF(!element) {
402 throw std::logic_error(
"Null element passed to populateBoolean function.");
405 if (!element->getFirstChildElement(
"offInPart")) {
409 if constexpr (std::is_base_of_v<Base, T>) {
410 const Base& instAsBase = *instance;
Base class to enforce polymorphism across all DOM classes.
Definition BaseClasses.h:60
virtual Cmper getPartId() const
Gets the partId for this instance (or SCORE_PARTID for score)
Definition BaseClasses.h:104
A utility class for managing deferred relationships between elements during document construction.
Definition FactoryBase.h:71
std::function< void(const dom::DocumentPtr &)> Resolver
A callable type representing a deferred relationship resolver.
Definition FactoryBase.h:80
void resolveAll(const dom::DocumentPtr &document)
Resolves all deferred relationships.
Definition FactoryBase.h:109
void addResolver(Resolver resolver, const std::string_view &key)
Adds a resolver function to the linker.
Definition FactoryBase.h:91
Factory base class.
Definition FactoryBase.h:133
static void getFieldFromXml(const XmlElementPtr &element, const std::string &nodeName, DataType &dataField, ParserFunc parserFunc, bool expected=false)
Helper function to check if a child exists and populate it if so.
Definition FactoryBase.h:146
static XmlElementPtr getFirstChildElement(const XmlElementPtr &element, const std::string &childElementName)
Helper function to throw when child element does not exist.
Definition FactoryBase.h:161
static std::optional< T > getOptionalChildTextAs(const XmlElementPtr &element, const std::string &childElementName, T defaultValue={})
Helper function to return std::nullopt when child element does not exist.
Definition FactoryBase.h:182
static std::optional< std::string > getOptionalChildText(const XmlElementPtr &element, const std::string &childElementName)
Helper function to return std::nullopt when child element does not exist.
Definition FactoryBase.h:171
Exception for unknown xml node errors. (Used when MUSX_THROW_ON_UNKNOWN_XML is defined....
Definition FactoryBase.h:57
@ Warning
Warning messages indicating potential issues.
static void log(LogLevel level, const std::string &message)
Logs a message with a specific severity level.
Definition Logger.h:87
The DOM (document object model) for musx files.
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:80
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition BaseClasses.h:51
Provides interfaces and optional implementations for traversing XML.
Definition PugiXmlImpl.h:33
std::tuple< const std::string_view, XmlElementPopulator< T > > XmlElementDescriptor
associates an xml node name with and XmlElementPopulator
Definition XmlInterface.h:125
std::shared_ptr< IXmlElement > XmlElementPtr
shared pointer to IXmlElement
Definition XmlInterface.h:121
object model for musx file (enigmaxml)
Definition BaseClasses.h:32