MUSX Document Model
Loading...
Searching...
No Matches
BaseClasses.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 <cassert>
25#include <filesystem>
26#include <set>
27#include <string_view>
28#include <unordered_set>
29#include <memory>
30
31#include "musx/dom/Fundamentals.h"
32#include "musx/xml/XmlInterface.h"
33#include "musx/util/Logger.h"
34#include "MusxInstance.h"
35
36namespace musx {
37
42namespace dom {
43
47class integrity_error : public std::runtime_error
48{
49public:
50 using std::runtime_error::runtime_error;
51};
52
53class Document;
55using DocumentPtr = std::shared_ptr<Document>;
57using DocumentWeakPtr = std::weak_ptr<Document>;
58
59#ifndef DOXYGEN_SHOULD_IGNORE_THIS
60class PartContextCloner {
61public:
62 template <typename T>
63 static void setRequestedPartId(const std::shared_ptr<T>& obj, Cmper partId)
64 {
65 obj->setRequestedPartId(partId);
66 }
67
68 template <typename T>
69 static std::shared_ptr<T> copyWithPartId(const std::shared_ptr<const T>& obj, Cmper partId)
70 {
71 auto result = std::make_shared<T>(*obj);
72 setRequestedPartId(result, partId);
73 return result;
74 }
75};
76#endif
77
82class Base
83{
84public:
86 using SharedNodes = std::set<std::string>;
87
90 enum class ShareMode
91 {
92 // this enum was giving Doxygen fits until I switched to using the slash-splat-bang-lt comment commands.
93 All,
94 Partial,
95 None
96 };
97
101 virtual ~Base() noexcept(false) = default;
102
109 {
110 auto document = m_document.lock();
111 MUSX_ASSERT_IF(!document) {
112 throw std::logic_error("Document pointer is no longer valid.");
113 }
114 return document;
115 }
116
124 Cmper getSourcePartId() const { return m_sourcePartId; }
125
129 ShareMode getShareMode() const { return m_shareMode; }
130
134 const SharedNodes& getUnlinkedNodes() const { return m_unlinkedNodes; }
135
139 void addUnlinkedNode(const std::string& nodeName)
140 {
141 m_unlinkedNodes.insert(nodeName);
142 }
143
154 virtual void integrityCheck([[maybe_unused]]const std::shared_ptr<Base>& ptrToThis) { }
155
166 virtual bool requireAllFields() const { return true; }
167
168protected:
176 Base(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode)
177 : m_document(document), m_sourcePartId(partId), m_shareMode(shareMode) {}
178
179 Base(const Base&) = default;
180 Base(Base&&) noexcept = default;
181
183 Base& operator=(const Base&) { return *this; }
185 Base& operator=(Base&&) noexcept { return *this; }
186
187private:
188 const DocumentWeakPtr m_document;
189 const Cmper m_sourcePartId;
190 const ShareMode m_shareMode;
191 SharedNodes m_unlinkedNodes;
192};
193
199class CommonClassBase : public Base
200{
201 Cmper getSourcePartId() const = delete;
202
203public:
210 : Base(document, SCORE_PARTID, ShareMode::All) {}
211
212protected:
213 // Because the part ID and share mode are hard-coded for this category of classes,
214 // these Base functions do not return useful results.
215 using Base::getShareMode;
217};
218
224{
225 Cmper getSourcePartId() const = delete;
226
227public:
234 : Base(parent->getDocument(), SCORE_PARTID, ShareMode::All), m_parent(parent)
235 {}
236
238 template <typename ParentClass = Base>
240 {
241 auto result = m_parent.lock();
242 MUSX_ASSERT_IF (!result) {
243 throw std::logic_error("Attempt to get parent of contained class, but the parent is no longer allocated.");
244 }
245 if constexpr (std::is_same_v<Base, ParentClass>) {
246 return result;
247 } else {
248 return std::dynamic_pointer_cast<const ParentClass>(result);
249 }
250 }
251
252private:
253 const MusxInstanceWeak<Base> m_parent;
254};
255
261class OptionsBase : public Base {
262 Cmper getSourcePartId() const = delete;
263
264protected:
272 OptionsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode)
273 : Base(document, partId, shareMode) {}
274};
275
282class OthersBase : public Base
283{
285 void setRequestedPartId(Cmper newValue) { m_requestedPartId = newValue; }
286
287 friend class PartContextCloner; // gives access to private setter
288
289protected:
299 OthersBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper, std::optional<Inci> inci = std::nullopt)
300 : Base(document, partId, shareMode), m_requestedPartId(partId), m_cmper(cmper), m_inci(inci) {}
301
304 {
305 if (this != &other) {
306 this->Base::operator=(other);
307 }
308 return *this;
309 }
311 OthersBase& operator=(OthersBase&& other) noexcept
312 {
313 if (this != &other) {
314 this->Base::operator=(other);
315 }
316 return *this;
317 }
318
319public:
320 OthersBase(const OthersBase&) = default;
321 OthersBase(OthersBase&&) noexcept = default;
322
328 Cmper getCmper() const { return m_cmper; }
329
335 std::optional<Inci> getInci() const { return m_inci; }
336
340 Cmper getRequestedPartId() const { return m_requestedPartId; }
341
342private:
343 Cmper m_requestedPartId{};
344 Cmper m_cmper;
345 std::optional<Inci> m_inci;
346};
347
351template <typename ElementType, size_t REQUIRED_SIZE = 0>
353{
354private:
355 virtual std::string_view xmlTag() const = 0;
356
357public:
359 explicit OthersArray(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper)
360 : OthersBase(document, partId, shareMode, cmper)
361 {
362 }
363
364 std::vector<ElementType> values;
366
368 void integrityCheck(const std::shared_ptr<Base>& ptrToThis) override
369 {
371 if constexpr (REQUIRED_SIZE > 0) {
372 const size_t originalSize = values.size();
373 values.resize(REQUIRED_SIZE); // resize first, in case MUSX_INTEGRITY_ERROR throws. (Avoid unreachable code warning.)
374 if (originalSize < REQUIRED_SIZE) {
375 MUSX_INTEGRITY_ERROR("Array with xml tag " + std::string(xmlTag()) + " and cmper " + std::to_string(getCmper())
376 + " has fewer than " + std::to_string(REQUIRED_SIZE) + " elements.");
377 }
378 }
379 }
380};
381
386class OthersName : public OthersBase
387{
388public:
390 explicit OthersName(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper)
391 : OthersBase(document, partId, shareMode, cmper)
392 {
393 }
394
395 std::string name;
396
398};
399
406class DetailsBase : public Base
407{
409 void setRequestedPartId(Cmper newValue) { m_requestedPartId = newValue; }
410
411 friend class PartContextCloner; // gives access to private setter
412
413protected:
424 DetailsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt)
425 : Base(document, partId, shareMode), m_requestedPartId(partId), m_cmper1(cmper1), m_cmper2(cmper2), m_inci(inci) {}
426
429 {
430 if (this != &other) {
431 this->Base::operator=(other);
432 }
433 return *this;
434 }
437 {
438 if (this != &other) {
439 this->Base::operator=(other);
440 }
441 return *this;
442 }
443
444public:
445 DetailsBase(const DetailsBase&) = default;
446 DetailsBase(DetailsBase&&) noexcept = default;
447
451 Cmper getCmper1() const { return m_cmper1; }
452
456 Cmper getCmper2() const { return m_cmper2; }
457
461 std::optional<Inci> getInci() const { return m_inci; }
462
466 Cmper getRequestedPartId() const { return m_requestedPartId; }
467
468private:
469 Cmper m_requestedPartId{};
470 Cmper m_cmper1;
471 Cmper m_cmper2;
472 std::optional<Inci> m_inci;
473};
474
479{
480protected:
490 EntryDetailsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, EntryNumber entnum, std::optional<Inci> inci = std::nullopt)
491 : DetailsBase(document, partId, shareMode, Cmper(entnum >> 16), Cmper(entnum & 0xffff), inci) {}
492
493public:
497 EntryNumber getEntryNumber() const { return EntryNumber(getCmper1()) << 16 | EntryNumber(getCmper2()); }
498
499private:
502};
503
507template <typename ElementType, size_t REQUIRED_SIZE = 0>
509{
510private:
511 virtual std::string_view xmlTag() const = 0;
512
513public:
515 explicit DetailsArray(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2)
516 : DetailsBase(document, partId, shareMode, cmper1, cmper2)
517 {
518 }
519
520 std::vector<ElementType> values;
522
524 void integrityCheck(const std::shared_ptr<Base>& ptrToThis) override
525 {
527 if constexpr (REQUIRED_SIZE > 0) {
528 const size_t originalSize = values.size();
529 values.resize(REQUIRED_SIZE); // resize first, in case MUSX_INTEGRITY_ERROR throws. (Avoid unreachable code warning.)
530 if (originalSize < REQUIRED_SIZE) {
531 MUSX_INTEGRITY_ERROR("Array with xml tag " + std::string(xmlTag()) + " and cmpers [" + std::to_string(getCmper1()) + ", " + std::to_string(getCmper2())
532 + "] has fewer than " + std::to_string(REQUIRED_SIZE) + " elements.");
533 }
534 }
535 }
536};
537
540{
541public:
543 virtual NoteNumber getNoteId() const = 0;
544
545protected:
548};
549
550class FontInfo;
556class TextsBase : public Base
557{
558 Cmper getSourcePartId() const = delete;
559
560public:
569 TextsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper textNumber)
570 : Base(document, partId, shareMode), m_textNumber(textNumber) {}
571
572 std::string text;
573
577 Cmper getTextNumber() const { return m_textNumber; }
578
582 void setTextNumber(Cmper textNumber) { m_textNumber = textNumber; }
583
590 util::EnigmaParsingContext getRawTextCtx(const MusxInstance<TextsBase>& ptrToThis, Cmper forPartId, std::optional<Cmper> forPageId = std::nullopt,
592
593private:
594 Cmper m_textNumber;
595};
596
597} // namespace dom
598} // namespace musx
Base class to enforce polymorphism across all DOM classes.
Definition BaseClasses.h:83
DocumentPtr getDocument() const
Gets a reference to the Document.
Definition BaseClasses.h:108
virtual bool requireAllFields() const
Specifies if the parser should alert (print or throw) when an unknown xml tag is found for this class...
Definition BaseClasses.h:166
Cmper getSourcePartId() const
Gets the source partId for this instance. If an instance is fully shared with the score,...
Definition BaseClasses.h:124
Base & operator=(const Base &)
no-op copy assignment operator allows subclasses to copy their values.
Definition BaseClasses.h:183
Base(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode)
Constructs the base class.
Definition BaseClasses.h:176
ShareMode getShareMode() const
Gets the sharing mode for this instance.
Definition BaseClasses.h:129
std::set< std::string > SharedNodes
The container type for shared nodes.
Definition BaseClasses.h:86
Base & operator=(Base &&) noexcept
no-op move assignment operator allows subclasses to move their values.
Definition BaseClasses.h:185
const SharedNodes & getUnlinkedNodes() const
Gets the unlinked nodes for this instance. (Only populated for ShareMode::Partial)
Definition BaseClasses.h:134
virtual void integrityCheck(const std::shared_ptr< Base > &ptrToThis)
Allows a class to determine if it has been properly contructed by the factory and fix issues that it ...
Definition BaseClasses.h:154
void addUnlinkedNode(const std::string &nodeName)
Adds a shared node for this instance.
Definition BaseClasses.h:139
ShareMode
Describes how this instance is shared between part and score.
Definition BaseClasses.h:91
virtual ~Base() noexcept(false)=default
Virtual destructor for polymorphic behavior.
Base(const Base &)=default
explicit default copy constructor
Base(Base &&) noexcept=default
explicit default move constructor
Base class for classes that are commonly used among others, details, entries, and/or texts....
Definition BaseClasses.h:200
CommonClassBase(const DocumentWeakPtr &document)
Constructs a CommonClassBase object.
Definition BaseClasses.h:209
Base class for classes that are contained by other classes.
Definition BaseClasses.h:224
ContainedClassBase(const MusxInstance< Base > &parent)
Constructs a ContainedClassBase object.
Definition BaseClasses.h:233
MusxInstance< ParentClass > getParent() const
Get the parent.
Definition BaseClasses.h:239
Template pattern for DetailsBase items consisting of an array of a single item.
Definition BaseClasses.h:509
void integrityCheck(const std::shared_ptr< Base > &ptrToThis) override
Override of Base::integrityCheck.
Definition BaseClasses.h:524
DetailsArray(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2)
Constructor function.
Definition BaseClasses.h:515
std::vector< ElementType > values
Definition BaseClasses.h:520
Base class for all "details" types.
Definition BaseClasses.h:407
DetailsBase & operator=(const DetailsBase &other)
Assignment operator delegates to base, preserving OthersBase state.
Definition BaseClasses.h:428
DetailsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2, std::optional< Inci > inci=std::nullopt)
Constructs a DetailsBase object.
Definition BaseClasses.h:424
DetailsBase(DetailsBase &&) noexcept=default
explicit default move constructor
Cmper getCmper1() const
Gets the cmper1 key value.
Definition BaseClasses.h:451
DetailsBase(const DetailsBase &)=default
explicit default copy constructor
DetailsBase & operator=(DetailsBase &&other) noexcept
Assignment operator delegates to base, preserving OthersBase state.
Definition BaseClasses.h:436
Cmper getRequestedPartId() const
If this instance was retrieved from an object pool, it contains the part ID that was used to retrieve...
Definition BaseClasses.h:466
Cmper getCmper2() const
Gets the cmper2 key value.
Definition BaseClasses.h:456
std::optional< Inci > getInci() const
Gets the optional array index (inci).
Definition BaseClasses.h:461
Represents a document object that encapsulates the entire EnigmaXML structure.
Definition Document.h:67
Base class for all "details" types that use entnum rather than cmper and cmper.
Definition BaseClasses.h:479
EntryDetailsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, EntryNumber entnum, std::optional< Inci > inci=std::nullopt)
Constructs a EntryDetailsBase object.
Definition BaseClasses.h:490
EntryNumber getEntryNumber() const
Gets the entnum key value.
Definition BaseClasses.h:497
Represents the default font settings for a particular element type.
Definition CommonClasses.h:123
Base class note details. Note details are entry details associated with a note ID.
Definition BaseClasses.h:540
virtual NoteNumber getNoteId() const =0
Required virtual function that returns the note id.
Base class for all "options" types.
Definition BaseClasses.h:261
OptionsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode)
Constructs the OptionsBase and validates XmlNodeName in the derived class.
Definition BaseClasses.h:272
Template pattern for OthersBase items consisting of an array of a single item.
Definition BaseClasses.h:353
void integrityCheck(const std::shared_ptr< Base > &ptrToThis) override
Override of Base::integrityCheck.
Definition BaseClasses.h:368
std::vector< ElementType > values
Definition BaseClasses.h:364
OthersArray(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper)
Constructor function.
Definition BaseClasses.h:359
Base class for all "others" types.
Definition BaseClasses.h:283
OthersBase & operator=(OthersBase &&other) noexcept
Assignment operator delegates to base, preserving OthersBase state.
Definition BaseClasses.h:311
OthersBase & operator=(const OthersBase &other)
Assignment operator delegates to base, preserving OthersBase state.
Definition BaseClasses.h:303
Cmper getRequestedPartId() const
If this instance was retrieved from an object pool, it contains the part ID that was used to retrieve...
Definition BaseClasses.h:340
std::optional< Inci > getInci() const
Gets the optional array index (inci).
Definition BaseClasses.h:335
OthersBase(OthersBase &&) noexcept=default
explicit default move constructor
OthersBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper, std::optional< Inci > inci=std::nullopt)
Constructs an OthersBase object.
Definition BaseClasses.h:299
Cmper getCmper() const
Gets the cmper key value.
Definition BaseClasses.h:328
OthersBase(const OthersBase &)=default
explicit default copy constructor
Many element names are embedded directly in top-level xml tags. This encapsulates that pattern.
Definition BaseClasses.h:387
static const xml::XmlElementArray< OthersName > & xmlMappingArray()
Required for musx::factory::FieldPopulator.
OthersName(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper)
Constructor function.
Definition BaseClasses.h:390
std::string name
the name
Definition BaseClasses.h:395
Base class for all text blocks.
Definition BaseClasses.h:557
Cmper getTextNumber() const
Returns the raw text number.
Definition BaseClasses.h:577
util::EnigmaParsingContext getRawTextCtx(const MusxInstance< TextsBase > &ptrToThis, Cmper forPartId, std::optional< Cmper > forPageId=std::nullopt, util::EnigmaString::TextInsertCallback defaultInsertFunc=util::EnigmaString::defaultInsertsCallback) const
Gets the raw text block.
Definition Texts.cpp:36
std::string text
Raw Enigma string (with Enigma string tags), encoded UTF-8.
Definition BaseClasses.h:572
TextsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper textNumber)
Constructs a TextsBase object.
Definition BaseClasses.h:569
void setTextNumber(Cmper textNumber)
Sets the raw text number.
Definition BaseClasses.h:582
Exception for integrity errors. (Used when MUSX_THROW_ON_INTEGRITY_CHECK_FAIL is defined....
Definition BaseClasses.h:48
Wrapper class for interpreting and rendering Enigma-style strings with insert handling.
Definition EnigmaString.h:415
static TextInsertCallback defaultInsertsCallback
Inserts callback to take all default insert subsitutions determined by parseEnigmaText.
Definition EnigmaString.h:339
std::function< std::optional< std::string >(const std::vector< std::string > &parsedCommand)> TextInsertCallback
Iteration function type that the parser calls back when it encounters an Enigma text insert that requ...
Definition EnigmaString.h:336
std::shared_ptr< const T > MusxInstance
Defines the type of a musx instance stored in a pool.
Definition MusxInstance.h:35
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:79
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:55
std::weak_ptr< Document > DocumentWeakPtr
Shared weak Document pointer.
Definition BaseClasses.h:57
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:69
uint16_t NoteNumber
Note identifier.
Definition Fundamentals.h:70
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition BaseClasses.h:55
std::weak_ptr< const T > MusxInstanceWeak
Defines a weak ptr to the type of a musx instance stored in a pool.
Definition MusxInstance.h:40
std::vector< XmlElementDescriptor< T > > XmlElementArray
an array type for XmlElementDescriptor instances.
Definition XmlInterface.h:127
object model for musx file (enigmaxml)
Definition BaseClasses.h:36