MUSX Document Model
Loading...
Searching...
No Matches
PoolFactory.h
1/*
2 * Copyright (C) 2025, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#include "musx/xml/XmlInterface.h"
25#include "musx/dom/BaseClasses.h"
26#include "musx/dom/ObjectPool.h"
27#include "musx/dom/Document.h"
28#include "FactoryBase.h"
29#include "TypeRegistry.h"
30
31#ifdef MUSX_DISPLAY_NODE_NAMES
32#include <set>
33#endif
34
35namespace musx {
36namespace factory {
37
49template<typename DerivedType, typename ObjectBase, typename PoolType>
51{
52// static_assert(std::is_base_of<PoolFactory<DerivedType, DerivedType>::value,
53// "ObjectBase must derive from PoolFactory.");
54
55public:
68 static std::shared_ptr<PoolType> create(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker)
69 {
70 auto pool = std::make_shared<PoolType>(document);
71
72#ifdef MUSX_DISPLAY_NODE_NAMES
73 std::string currentTag;
74 size_t currentTagCount = 0;
78#endif
79
80 for (auto childElement = element->getFirstChildElement(); childElement; childElement = childElement->getNextSibling()) {
81 if (auto instanceInfo = DerivedType::extractFromXml(childElement, document, elementLinker, pool)) {
82#ifdef MUSX_DISPLAY_NODE_NAMES
83 if (currentTag != childElement->getTagName()) {
84 if (!currentTag.empty()) {
85 util::Logger::log(util::Logger::LogLevel::Verbose, " " + currentTag + " [" + std::to_string(currentTagCount) + "]");
86 }
87 currentTag = childElement->getTagName();
88 currentTagCount = 0;
89 }
90 currentTagCount++;
91#endif
92 MUSX_ASSERT_IF(childElement->getTagName() != instanceInfo->xmlNodeName) {
93 throw std::logic_error("Instance of " + std::string(instanceInfo->xmlNodeName) + " does not match xml tag " + element->getTagName());
94 }
95 auto typedPtr = std::dynamic_pointer_cast<ObjectBase>(instanceInfo->instance);
96 MUSX_ASSERT_IF(!typedPtr) {
97 throw std::logic_error("Unable to cast instance to correct type for " + std::string(instanceInfo->xmlNodeName));
98 }
99 if constexpr (std::is_same_v<PoolType, EntryPool>) {
100 pool->add(typedPtr->getEntryNumber(), typedPtr);
101 } else {
102 pool->add(instanceInfo->xmlNodeName, typedPtr);
103 }
104 }
105 }
106
107#ifdef MUSX_DISPLAY_NODE_NAMES
108 if (!currentTag.empty() && currentTagCount != 0) {
109 util::Logger::log(util::Logger::LogLevel::Verbose, " " + currentTag + " [" + std::to_string(currentTagCount) + "]");
110 }
111#endif
112
113 return pool;
114 }
115};
116
124class OptionsFactory : public PoolFactory<OptionsFactory, dom::OptionsBase, dom::OptionsPool>
125{
126public:
128
141 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const OptionsPoolPtr& pool)
142 {
143 return RegisteredOptions::createInstance(pool, element, elementLinker, document);
144 }
145};
146
154class OthersFactory : public PoolFactory<OthersFactory, dom::OthersBase, dom::OthersPool>
155{
156public:
158
172 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const OthersPoolPtr& pool)
173 {
174 auto cmperAttribute = element->findAttribute("cmper");
175 if (!cmperAttribute) {
176 throw std::invalid_argument("missing cmper for others element " + element->getTagName());
177 }
178 auto inciAttribute = element->findAttribute("inci");
179 if (inciAttribute) {
180 return RegisteredOthers::createInstance(pool, element, elementLinker,
181 document, cmperAttribute->getValueAs<dom::Cmper>(), inciAttribute->getValueAs<dom::Inci>());
182 }
183 else {
184 return RegisteredOthers::createInstance(pool, element, elementLinker,
185 document, cmperAttribute->getValueAs<dom::Cmper>());
186 }
187 }
188};
189
197class DetailsFactory : public PoolFactory<DetailsFactory, dom::DetailsBase, dom::DetailsPool>
198{
199public:
201
215 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const DetailsPoolPtr& pool)
216 {
217 std::optional<dom::Inci> inci;
218 if (auto inciAttribute = element->findAttribute("inci")) {
219 inci = inciAttribute->getValueAs<dom::Inci>();
220 }
221 if (auto entnumAttribute = element->findAttribute("entnum")) {
222 if (inci.has_value()) {
223 return RegisteredDetails::createInstance(pool, element, elementLinker,
224 document, entnumAttribute->getValueAs<dom::EntryNumber>(), inci.value());
225 } else {
226 return RegisteredDetails::createInstance(pool, element, elementLinker,
227 document, entnumAttribute->getValueAs<dom::EntryNumber>());
228 }
229 }
230 auto cmper1Attribute = element->findAttribute("cmper1");
231 if (!cmper1Attribute) {
232 throw std::invalid_argument("missing cmper1 for details element " + element->getTagName());
233 }
234 auto cmper2Attribute = element->findAttribute("cmper2");
235 if (!cmper2Attribute) {
236 throw std::invalid_argument("missing cmper2 for details element " + element->getTagName());
237 }
238 if (inci.has_value()) {
239 return RegisteredDetails::createInstance(pool, element, elementLinker,
240 document, cmper1Attribute->getValueAs<dom::Cmper>(), cmper2Attribute->getValueAs<dom::Cmper>(), inci.value());
241 } else {
242 return RegisteredDetails::createInstance(pool, element, elementLinker,
243 document, cmper1Attribute->getValueAs<dom::Cmper>(), cmper2Attribute->getValueAs<dom::Cmper>());
244 }
245 }
246};
247
254class EntryFactory : public PoolFactory<EntryFactory, dom::Entry, dom::EntryPool>
255{
256public:
258
271 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const EntryPoolPtr& pool)
272 {
273 auto entnumAttr = element->findAttribute("entnum");
274 if (!entnumAttr) {
275 throw std::invalid_argument("missing entum attribute for entry");
276 }
277 auto prevAttr = element->findAttribute("prev");
278 if (!entnumAttr) {
279 throw std::invalid_argument("missing prev attribute for entry");
280 }
281 auto nextAttr = element->findAttribute("next");
282 if (!entnumAttr) {
283 throw std::invalid_argument("missing next attribute for entry");
284 }
285 return RegisteredEntries::createInstance(pool, element, elementLinker, document,
286 entnumAttr->getValueAs<EntryNumber>(),
287 prevAttr->getValueAs<EntryNumber>(),
288 nextAttr->getValueAs<EntryNumber>());
289 }
290};
291
299class TextsFactory : public PoolFactory<TextsFactory, dom::TextsBase, dom::TextsPool>
300{
301public:
303
317 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const TextsPoolPtr& pool)
318 {
319 auto textAttributeName = [element]() -> std::string {
320 if (element->getTagName() == texts::FileInfoText::XmlNodeName) {
321 return "type";
322 }
323 return "number";
324 }();
325 auto textAttribute = element->findAttribute(textAttributeName);
326 if (!textAttribute) {
327 throw std::invalid_argument("Element <" + element->getTagName() + "> does not have attribute " + textAttributeName);
328 }
329 auto textNumber = [textAttribute]() -> Cmper {
330 if (textAttribute->getName() == "type") {
331 return toCmper(textAttribute->getValue());
332 }
333 return textAttribute->getValueAs<Cmper>();
334 }();
335 return RegisteredTexts::createInstance(pool, element, elementLinker, document, textNumber);
336 }
337
338private:
339 static Cmper toCmper(const std::string& type)
340 {
341 using TextType = texts::FileInfoText::TextType;
342
343 static const std::unordered_map<std::string_view, TextType> typeMap = {
344 {"title", TextType::Title},
345 {"composer", TextType::Composer},
346 {"copyright", TextType::Copyright},
347 {"description", TextType::Description},
348 {"lyricist", TextType::Lyricist},
349 {"arranger", TextType::Arranger},
350 {"subtitle", TextType::Subtitle}
351 };
352
353 auto it = typeMap.find(type);
354 if (it != typeMap.end()) {
355 return Cmper(it->second);
356 }
357
358 throw std::invalid_argument("Unknown type attribute value for <fileInfo> node: " + type);
359 }
360};
361
362} // namespace factory
363} // namespace musx
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Texts.h:114
TextType
Represents various text types for the file header.
Definition Texts.h:89
Factory class for creating Details objects from XML.
Definition PoolFactory.h:198
static auto extractFromXml(const XmlElementPtr &element, const dom::DocumentPtr &document, ElementLinker &elementLinker, const DetailsPoolPtr &pool)
Extracts an OthersBase object from an XML element.
Definition PoolFactory.h:215
A utility class for managing deferred relationships between elements during document construction.
Definition FactoryBase.h:71
Factory class for creating Entry objects from XML.
Definition PoolFactory.h:255
static auto extractFromXml(const XmlElementPtr &element, const dom::DocumentPtr &document, ElementLinker &elementLinker, const EntryPoolPtr &pool)
Extracts an Entry object from an XML element.
Definition PoolFactory.h:271
Factory base class.
Definition FactoryBase.h:133
Factory class for creating Options objects from XML.
Definition PoolFactory.h:125
static auto extractFromXml(const XmlElementPtr &element, const dom::DocumentPtr &document, ElementLinker &elementLinker, const OptionsPoolPtr &pool)
Extracts an OptionsBase object from an XML element.
Definition PoolFactory.h:141
Factory class for creating Others objects from XML.
Definition PoolFactory.h:155
static auto extractFromXml(const XmlElementPtr &element, const dom::DocumentPtr &document, ElementLinker &elementLinker, const OthersPoolPtr &pool)
Extracts an OthersBase object from an XML element.
Definition PoolFactory.h:172
Factory class for creating a dom::ObjectPool from XML.
Definition PoolFactory.h:51
static std::shared_ptr< PoolType > create(const XmlElementPtr &element, const dom::DocumentPtr &document, ElementLinker &elementLinker)
Creates a OthersPool object from an XML element.
Definition PoolFactory.h:68
Factory class for creating Texts objects from XML.
Definition PoolFactory.h:300
static auto extractFromXml(const XmlElementPtr &element, const dom::DocumentPtr &document, ElementLinker &elementLinker, const TextsPoolPtr &pool)
Extracts a TextsBase object from an XML element.
Definition PoolFactory.h:317
static std::optional< CreatedInstanceInfo > createInstance(const PoolPtr &pool, const XmlElementPtr &node, ElementLinker &elementLinker, const DocumentPtr &document, Args &&... args)
Creates an instance of the registered type corresponding to the provided node name.
Definition TypeRegistry.h:147
@ Verbose
Informational messages that should only displayed when verbose logging is requested.
static void log(LogLevel level, const std::string &message)
Logs a message with a specific severity level.
Definition Logger.h:87
std::shared_ptr< OthersPool > OthersPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:445
std::shared_ptr< DetailsPool > DetailsPoolPtr
Shared DetailsPool pointer.
Definition ObjectPool.h:532
int16_t Inci
Enigma "incident" key type.
Definition Fundamentals.h:56
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:55
std::shared_ptr< TextsPool > TextsPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:604
std::shared_ptr< OptionsPool > OptionsPoolPtr
Shared OptionsPool pointer.
Definition ObjectPool.h:392
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:69
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition BaseClasses.h:55
std::shared_ptr< EntryPool > EntryPoolPtr
Shared EntryPool pointer.
Definition ObjectPool.h:565
std::shared_ptr< IXmlElement > XmlElementPtr
shared pointer to IXmlElement
Definition XmlInterface.h:121
object model for musx file (enigmaxml)
Definition BaseClasses.h:36