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 "FactoryBase.h"
25#include "TypeRegistry.h"
26#include "musx/xml/XmlInterface.h"
27#include "musx/dom/BaseClasses.h"
28#include "musx/dom/ObjectPool.h"
29#include "musx/dom/Document.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>();
71
72#ifdef MUSX_DISPLAY_NODE_NAMES
73 std::set<std::string> alreadyDisplayed;
77#endif
78
79 for (auto childElement = element->getFirstChildElement(); childElement; childElement = childElement->getNextSibling()) {
80 auto basePtr = DerivedType::extractFromXml(childElement, document, elementLinker, pool);
81 if (basePtr) {
82#ifdef MUSX_DISPLAY_NODE_NAMES
83 auto it = alreadyDisplayed.find(childElement->getTagName());
84 if (it == alreadyDisplayed.end()) {
85 util::Logger::log(util::Logger::LogLevel::Verbose, " " + childElement->getTagName());
86 alreadyDisplayed.emplace(childElement->getTagName());
87 }
88#endif
89 auto typedPtr = std::dynamic_pointer_cast<ObjectBase>(basePtr);
90 assert(typedPtr); // program bug if null
91 if constexpr (std::is_same_v<PoolType, EntryPool>) {
92 pool->add(typedPtr->getEntryNumber(), typedPtr);
93 } else {
94 pool->add(childElement->getTagName(), typedPtr);
95 }
96 }
97 }
98
99 return pool;
100 }
101};
102
110class OptionsFactory : public PoolFactory<OptionsFactory, dom::OptionsBase, dom::OptionsPool>
111{
112public:
114
127 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const OptionsPoolPtr& pool)
128 {
129 return RegisteredOptions::createInstance(pool, element, elementLinker, document);
130 }
131};
132
140class OthersFactory : public PoolFactory<OthersFactory, dom::OthersBase, dom::OthersPool>
141{
142public:
144
158 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const OthersPoolPtr& pool)
159 {
160 auto cmperAttribute = element->findAttribute("cmper");
161 if (!cmperAttribute) {
162 throw std::invalid_argument("missing cmper for others element " + element->getTagName());
163 }
164 auto inciAttribute = element->findAttribute("inci");
165 if (inciAttribute) {
166 return RegisteredOthers::createInstance(pool, element, elementLinker,
167 document, cmperAttribute->getValueAs<dom::Cmper>(), inciAttribute->getValueAs<dom::Inci>());
168 }
169 else {
170 return RegisteredOthers::createInstance(pool, element, elementLinker,
171 document, cmperAttribute->getValueAs<dom::Cmper>());
172 }
173 }
174};
175
183class DetailsFactory : public PoolFactory<DetailsFactory, dom::DetailsBase, dom::DetailsPool>
184{
185public:
187
201 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const DetailsPoolPtr& pool)
202 {
203 std::optional<dom::Inci> inci;
204 if (auto inciAttribute = element->findAttribute("inci")) {
205 inci = inciAttribute->getValueAs<dom::Inci>();
206 }
207 if (auto entnumAttribute = element->findAttribute("entnum")) {
208 if (inci.has_value()) {
209 return RegisteredDetails::createInstance(pool, element, elementLinker,
210 document, entnumAttribute->getValueAs<dom::EntryNumber>(), inci.value());
211 } else {
212 return RegisteredDetails::createInstance(pool, element, elementLinker,
213 document, entnumAttribute->getValueAs<dom::EntryNumber>());
214 }
215 }
216 auto cmper1Attribute = element->findAttribute("cmper1");
217 if (!cmper1Attribute) {
218 throw std::invalid_argument("missing cmper1 for details element " + element->getTagName());
219 }
220 auto cmper2Attribute = element->findAttribute("cmper2");
221 if (!cmper2Attribute) {
222 throw std::invalid_argument("missing cmper2 for details element " + element->getTagName());
223 }
224 if (inci.has_value()) {
225 return RegisteredDetails::createInstance(pool, element, elementLinker,
226 document, cmper1Attribute->getValueAs<dom::Cmper>(), cmper2Attribute->getValueAs<dom::Cmper>(), inci.value());
227 } else {
228 return RegisteredDetails::createInstance(pool, element, elementLinker,
229 document, cmper1Attribute->getValueAs<dom::Cmper>(), cmper2Attribute->getValueAs<dom::Cmper>());
230 }
231 }
232};
233
240class EntryFactory : public PoolFactory<EntryFactory, dom::Entry, dom::EntryPool>
241{
242public:
244
257 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const EntryPoolPtr& pool)
258 {
259 auto entnumAttr = element->findAttribute("entnum");
260 if (!entnumAttr) {
261 throw std::invalid_argument("missing entum attribute for entry");
262 }
263 auto prevAttr = element->findAttribute("prev");
264 if (!entnumAttr) {
265 throw std::invalid_argument("missing prev attribute for entry");
266 }
267 auto nextAttr = element->findAttribute("next");
268 if (!entnumAttr) {
269 throw std::invalid_argument("missing next attribute for entry");
270 }
271 return RegisteredEntries::createInstance(pool, element, elementLinker, document,
272 entnumAttr->getValueAs<EntryNumber>(),
273 prevAttr->getValueAs<EntryNumber>(),
274 nextAttr->getValueAs<EntryNumber>());
275 }
276};
277
285class TextsFactory : public PoolFactory<TextsFactory, dom::TextsBase, dom::TextsPool>
286{
287public:
289
303 static auto extractFromXml(const XmlElementPtr& element, const dom::DocumentPtr& document, ElementLinker& elementLinker, const TextsPoolPtr& pool)
304 {
305 auto textAttributeName = [element]() -> std::string {
306 if (element->getTagName() == texts::FileInfoText::XmlNodeName) {
307 return "type";
308 }
309 return "number";
310 }();
311 auto textAttribute = element->findAttribute(textAttributeName);
312 if (!textAttribute) {
313 throw std::invalid_argument("Element <" + element->getTagName() + "> does not have attribute " + textAttributeName);
314 }
315 auto textNumber = [textAttribute]() -> Cmper {
316 if (textAttribute->getName() == "type") {
317 return toCmper(textAttribute->getValue());
318 }
319 return textAttribute->getValueAs<Cmper>();
320 }();
321 return RegisteredTexts::createInstance(pool, element, elementLinker, document, textNumber);
322 }
323
324private:
325 static Cmper toCmper(const std::string& type) {
326 using TextType = texts::FileInfoText::TextType;
327
328 if (type == "title") {
329 return Cmper(TextType::Title);
330 } else if (type == "composer") {
331 return Cmper(TextType::Composer);
332 } else if (type == "copyright") {
333 return Cmper(TextType::Copyright);
334 } else if (type == "description") {
335 return Cmper(TextType::Description);
336 } else if (type == "lyricist") {
337 return Cmper(TextType::Lyricist);
338 } else if (type == "arranger") {
339 return Cmper(TextType::Arranger);
340 } else if (type == "subtitle") {
341 return Cmper(TextType::Subtitle);
342 } else {
343 throw std::invalid_argument("Unknown type attribute value for <fileInfo> node: " + type);
344 }
345 }
346};
347
348} // namespace factory
349} // namespace musx
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Texts.h:110
TextType
Represents various text types for the file header.
Definition Texts.h:87
Factory class for creating Details objects from XML.
Definition PoolFactory.h:184
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:201
A utility class for managing deferred relationships between elements during document construction.
Definition FactoryBase.h:70
Factory class for creating Entry objects from XML.
Definition PoolFactory.h:241
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:257
Factory base class.
Definition FactoryBase.h:132
Factory class for creating Options objects from XML.
Definition PoolFactory.h:111
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:127
Factory class for creating Others objects from XML.
Definition PoolFactory.h:141
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:158
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:286
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:303
static std::shared_ptr< Base > 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:106
@ 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:327
std::shared_ptr< DetailsPool > DetailsPoolPtr
Shared DetailsPool pointer.
Definition ObjectPool.h:378
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:420
std::shared_ptr< OptionsPool > OptionsPoolPtr
Shared OptionsPool pointer.
Definition ObjectPool.h:303
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:68
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition BaseClasses.h:55
std::shared_ptr< EntryPool > EntryPoolPtr
Shared EntryPool pointer.
Definition ObjectPool.h:394
std::shared_ptr< IXmlElement > XmlElementPtr
shared pointer to IXmlElement
Definition XmlInterface.h:121
object model for musx file (enigmaxml)
Definition BaseClasses.h:32