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 <functional>
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/EnigmaString.h"
34#include "musx/util/Logger.h"
35#include "DocumentElement.h"
36#include "MusxInstance.h"
37
38namespace musx {
39
44namespace dom {
45
49class integrity_error : public std::runtime_error
50{
51public:
52 using std::runtime_error::runtime_error;
53};
54
55#ifndef DOXYGEN_SHOULD_IGNORE_THIS
56template <typename T>
57struct PartContextRebinder
58{
59 static void rebind(const std::shared_ptr<T>&) {}
60};
61
62class PartContextCloner {
63public:
64 template <typename T>
65 static void setRequestedPartId(const std::shared_ptr<T>& obj, Cmper partId)
66 {
67 obj->setRequestedPartId(partId);
68 }
69
70 template <typename T>
71 static std::shared_ptr<T> copyWithPartId(const std::shared_ptr<const T>& obj, Cmper partId)
72 {
73 auto result = std::make_shared<T>(*obj);
74 setRequestedPartId(result, partId);
75 PartContextRebinder<T>::rebind(result);
76 return result;
77 }
78};
79#endif
80
88{
89 Cmper getPartId() const = delete;
90
91public:
94 enum class ShareMode
95 {
96 All,
97 Partial,
98 None
99 };
100
109
113 ShareMode getShareMode() const { return m_shareMode; }
114
116 virtual void integrityCheck([[maybe_unused]] const std::shared_ptr<EnigmaBase>& ptrToThis) { }
117
119 virtual bool requireAllFields() const { return true; }
120
121protected:
129 EnigmaBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode)
130 : DocumentElement(document, partId), m_shareMode(shareMode) {}
131
132 EnigmaBase(const EnigmaBase&) = default;
133 EnigmaBase(EnigmaBase&&) noexcept = default;
134
137 EnigmaBase& operator=(const EnigmaBase&) { return *this; }
138
141 EnigmaBase& operator=(EnigmaBase&&) noexcept { return *this; }
142
143private:
144 const ShareMode m_shareMode;
145};
146
150using ImportObjectCallback = std::function<void(const EnigmaBase&)>;
151
158{
159 Cmper getSourcePartId() const = delete;
160
161public:
168 : EnigmaBase(document, SCORE_PARTID, ShareMode::All) {}
169
170protected:
171 // Because the part ID and share mode are hard-coded for this category of classes,
172 // these EnigmaBase functions do not return useful results.
174};
175
181{
182 Cmper getSourcePartId() const = delete;
183
184protected:
185 // A shallow copy of an owning OthersBase or DetailsBase leaves contained objects parented to
186 // the source instance. If that distinction matters, the owner must use PartContextRebinder.
187 // PartContextRebinder<details::CenterShape> is a simple example.
188
191 {
192 if (this != &other) {
193 this->EnigmaBase::operator=(other);
194 }
195 return *this;
196 }
197
200 {
201 if (this != &other) {
202 this->EnigmaBase::operator=(std::move(other));
203 }
204 return *this;
205 }
206
207public:
214 : EnigmaBase(parent->getDocument(), SCORE_PARTID, ShareMode::All), m_parent(parent)
215 {}
216
218 template <typename ParentClass = EnigmaBase>
220 {
221 auto result = m_parent.lock();
222 MUSX_ASSERT_IF (!result) {
223 throw std::logic_error("Attempt to get parent of contained class, but the parent is no longer allocated.");
224 }
225 if constexpr (std::is_same_v<EnigmaBase, ParentClass>) {
226 return result;
227 } else {
228 return std::dynamic_pointer_cast<const ParentClass>(result);
229 }
230 }
231
232private:
233 const MusxInstanceWeak<EnigmaBase> m_parent;
234};
235
241class OptionsBase : public EnigmaBase {
242 Cmper getSourcePartId() const = delete;
243
244protected:
252 OptionsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode)
253 : EnigmaBase(document, partId, shareMode) {}
254};
255
262class OthersBase : public EnigmaBase
263{
265 void setRequestedPartId(Cmper newValue) { m_requestedPartId = newValue; }
266
267 friend class PartContextCloner; // gives access to private setter
268
269protected:
279 OthersBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper, std::optional<Inci> inci = std::nullopt)
280 : EnigmaBase(document, partId, shareMode), m_requestedPartId(partId), m_cmper(cmper), m_inci(inci) {}
281
285 {
286 if (this != &other) {
287 this->EnigmaBase::operator=(other);
288 }
289 return *this;
290 }
293 OthersBase& operator=(OthersBase&& other) noexcept
294 {
295 if (this != &other) {
296 this->EnigmaBase::operator=(other);
297 }
298 return *this;
299 }
300
301public:
302 OthersBase(const OthersBase&) = default;
303 OthersBase(OthersBase&&) noexcept = default;
304
310 Cmper getCmper() const { return m_cmper; }
311
317 std::optional<Inci> getInci() const { return m_inci; }
318
322 Cmper getRequestedPartId() const { return m_requestedPartId; }
323
324private:
325 Cmper m_requestedPartId{};
326 Cmper m_cmper;
327 std::optional<Inci> m_inci;
328};
329
333template <typename ElementType, size_t REQUIRED_SIZE = 0>
335{
336private:
337 virtual std::string_view xmlTag() const = 0;
338
339public:
341 explicit OthersArray(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper)
342 : OthersBase(document, partId, shareMode, cmper)
343 {
344 }
345
346 std::vector<ElementType> values;
348
351 bool containsValue(const ElementType& value) const noexcept
352 {
353 const auto& v = this->values;
354 return std::find(v.begin(), v.end(), value) != v.end();
355 }
356
358 void integrityCheck(const std::shared_ptr<EnigmaBase>& ptrToThis) override
359 {
361 if constexpr (REQUIRED_SIZE > 0) {
362 const size_t originalSize = values.size();
363 values.resize(REQUIRED_SIZE); // resize first, in case MUSX_INTEGRITY_ERROR throws. (Avoid unreachable code warning.)
364 if (originalSize < REQUIRED_SIZE) {
365 MUSX_INTEGRITY_ERROR("Array with xml tag " + std::string(xmlTag()) + " and cmper " + std::to_string(getCmper())
366 + " has fewer than " + std::to_string(REQUIRED_SIZE) + " elements.");
367 }
368 }
369 }
370};
371
376class OthersName : public OthersBase
377{
378public:
380 explicit OthersName(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper)
381 : OthersBase(document, partId, shareMode, cmper)
382 {
383 }
384
385 std::string name;
386
388};
389
397{
399 void setRequestedPartId(Cmper newValue) { m_requestedPartId = newValue; }
400
401 friend class PartContextCloner; // gives access to private setter
402
403protected:
414 DetailsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt)
415 : EnigmaBase(document, partId, shareMode), m_requestedPartId(partId), m_cmper1(cmper1), m_cmper2(cmper2), m_inci(inci) {}
416
420 {
421 if (this != &other) {
422 this->EnigmaBase::operator=(other);
423 }
424 return *this;
425 }
429 {
430 if (this != &other) {
431 this->EnigmaBase::operator=(other);
432 }
433 return *this;
434 }
435
436public:
437 DetailsBase(const DetailsBase&) = default;
438 DetailsBase(DetailsBase&&) noexcept = default;
439
443 Cmper getCmper1() const { return m_cmper1; }
444
448 Cmper getCmper2() const { return m_cmper2; }
449
453 std::optional<Inci> getInci() const { return m_inci; }
454
458 Cmper getRequestedPartId() const { return m_requestedPartId; }
459
460private:
461 Cmper m_requestedPartId{};
462 Cmper m_cmper1;
463 Cmper m_cmper2;
464 std::optional<Inci> m_inci;
465};
466
471{
472public:
475 enum class StemSelection
476 {
477 MatchEntry,
478 UpStem,
479 DownStem,
480 Any
481 };
482
483protected:
493 EntryDetailsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, EntryNumber entnum, std::optional<Inci> inci = std::nullopt)
494 : DetailsBase(document, partId, shareMode, Cmper(entnum >> 16), Cmper(entnum & 0xffff), inci) {}
495
503 template <typename EDUP, typename EDDOWN, typename EDBASE>
504 static MusxInstance<EDBASE> getStemDependentDetail(const EntryInfoPtr& entryInfo, StemSelection stemSelection);
505
506public:
510 EntryNumber getEntryNumber() const { return EntryNumber(getCmper1()) << 16 | EntryNumber(getCmper2()); }
511
512private:
515};
516
520template <typename ElementType, size_t REQUIRED_SIZE = 0>
522{
523private:
524 virtual std::string_view xmlTag() const = 0;
525
526public:
528 explicit DetailsArray(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2)
529 : DetailsBase(document, partId, shareMode, cmper1, cmper2)
530 {
531 }
532
533 std::vector<ElementType> values;
535
537 void integrityCheck(const std::shared_ptr<EnigmaBase>& ptrToThis) override
538 {
540 if constexpr (REQUIRED_SIZE > 0) {
541 const size_t originalSize = values.size();
542 values.resize(REQUIRED_SIZE); // resize first, in case MUSX_INTEGRITY_ERROR throws. (Avoid unreachable code warning.)
543 if (originalSize < REQUIRED_SIZE) {
544 MUSX_INTEGRITY_ERROR("Array with xml tag " + std::string(xmlTag()) + " and cmpers [" + std::to_string(getCmper1()) + ", " + std::to_string(getCmper2())
545 + "] has fewer than " + std::to_string(REQUIRED_SIZE) + " elements.");
546 }
547 }
548 }
549};
550
553{
554public:
556 virtual NoteNumber getNoteId() const = 0;
557
558protected:
561};
562
563class FontInfo;
569class TextsBase : public EnigmaBase
570{
571 Cmper getSourcePartId() const = delete;
572
573public:
582 TextsBase(const DocumentWeakPtr& document, Cmper partId, ShareMode shareMode, Cmper textNumber)
583 : EnigmaBase(document, partId, shareMode), m_textNumber(textNumber) {}
584
585 TextsBase(const TextsBase&) = default;
586 TextsBase(TextsBase&&) noexcept = default;
587
588protected:
591 TextsBase& operator=(const TextsBase& other)
592 {
593 if (this != &other) {
594 this->EnigmaBase::operator=(other);
595 text = other.text;
596 }
597 return *this;
598 }
599
602 TextsBase& operator=(TextsBase&& other) noexcept
603 {
604 if (this != &other) {
605 this->EnigmaBase::operator=(std::move(other));
606 text = std::move(other.text);
607 }
608 return *this;
609 }
610
611public:
612 std::string text;
613
617 Cmper getTextNumber() const { return m_textNumber; }
618
622 void setTextNumber(Cmper textNumber) { m_textNumber = textNumber; }
623
630 util::EnigmaParsingContext getRawTextCtx(const MusxInstance<TextsBase>& ptrToThis, Cmper forPartId, std::optional<Cmper> forPageId = std::nullopt,
632
633private:
634 Cmper m_textNumber;
635};
636
637} // namespace dom
638} // namespace musx
EnigmaBase class for classes that are commonly used among others, details, entries,...
Definition BaseClasses.h:158
CommonClassBase(const DocumentWeakPtr &document)
Constructs a CommonClassBase object.
Definition BaseClasses.h:167
EnigmaBase class for classes that are contained by other classes.
Definition BaseClasses.h:181
ContainedClassBase & operator=(ContainedClassBase &&other) noexcept
Move assignment preserves the destination DOM instance's document and parent.
Definition BaseClasses.h:199
ContainedClassBase(const MusxInstance< EnigmaBase > &parent)
Constructs a ContainedClassBase object.
Definition BaseClasses.h:213
ContainedClassBase & operator=(const ContainedClassBase &other)
Copy assignment preserves the destination DOM instance's document and parent.
Definition BaseClasses.h:190
MusxInstance< ParentClass > getParent() const
Get the parent.
Definition BaseClasses.h:219
Template pattern for DetailsBase items consisting of an array of a single item.
Definition BaseClasses.h:522
DetailsArray(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper1, Cmper cmper2)
Constructor function.
Definition BaseClasses.h:528
std::vector< ElementType > values
Definition BaseClasses.h:533
void integrityCheck(const std::shared_ptr< EnigmaBase > &ptrToThis) override
Override of EnigmaBase::integrityCheck.
Definition BaseClasses.h:537
EnigmaBase class for all "details" types.
Definition BaseClasses.h:397
DetailsBase & operator=(const DetailsBase &other)
Copy assignment preserves the destination DOM instance's document, source and requested part IDs,...
Definition BaseClasses.h:419
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:414
DetailsBase(DetailsBase &&) noexcept=default
explicit default move constructor
Cmper getCmper1() const
Gets the cmper1 key value.
Definition BaseClasses.h:443
DetailsBase(const DetailsBase &)=default
explicit default copy constructor
DetailsBase & operator=(DetailsBase &&other) noexcept
Move assignment preserves the destination DOM instance's document, source and requested part IDs,...
Definition BaseClasses.h:428
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:458
Cmper getCmper2() const
Gets the cmper2 key value.
Definition BaseClasses.h:448
std::optional< Inci > getInci() const
Gets the optional array index (inci).
Definition BaseClasses.h:453
Base for DOM classes that belong to a Document.
Definition DocumentElement.h:46
DocumentPtr getDocument() const
Gets a reference to the Document.
Definition DocumentElement.h:58
Cmper getPartId() const
Gets the part id associated with this instance.
Definition DocumentElement.h:70
Base for DOM classes that are represented in EnigmaData.
Definition BaseClasses.h:88
virtual bool requireAllFields() const
Returns true if all fields are required for valid input.
Definition BaseClasses.h:119
EnigmaBase & operator=(const EnigmaBase &)
Copy assignment preserves the destination DOM instance's document, source part, and sharing mode.
Definition BaseClasses.h:137
ShareMode getShareMode() const
Gets the sharing mode for this instance.
Definition BaseClasses.h:113
EnigmaBase(const EnigmaBase &)=default
explicit default copy constructor
EnigmaBase & operator=(EnigmaBase &&) noexcept
Move assignment preserves the destination DOM instance's document, source part, and sharing mode.
Definition BaseClasses.h:141
EnigmaBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode)
Constructs the base class.
Definition BaseClasses.h:129
ShareMode
Describes how this instance is shared between part and score.
Definition BaseClasses.h:95
EnigmaBase(EnigmaBase &&) noexcept=default
explicit default move constructor
Cmper getSourcePartId() const
Gets the source partId for this instance. If an instance is fully shared with the score,...
Definition BaseClasses.h:108
virtual void integrityCheck(const std::shared_ptr< EnigmaBase > &ptrToThis)
Performs a final consistency check after population.
Definition BaseClasses.h:116
EnigmaBase class for all "details" types that use entnum rather than cmper and cmper.
Definition BaseClasses.h:471
EntryDetailsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, EntryNumber entnum, std::optional< Inci > inci=std::nullopt)
Constructs a EntryDetailsBase object.
Definition BaseClasses.h:493
EntryNumber getEntryNumber() const
Gets the entnum key value.
Definition BaseClasses.h:510
StemSelection
The options for choosing which version to retrieve for stem-specific details.
Definition BaseClasses.h:476
@ DownStem
retrieve the downstem version
@ MatchEntry
match entry's stem direction
@ UpStem
retrieve the upstem version
@ Any
retrieve the first one encountered (starting with upstem)
static MusxInstance< EDBASE > getStemDependentDetail(const EntryInfoPtr &entryInfo, StemSelection stemSelection)
Implement retrieval of stem-specific details. (Actual functions are defined per stem-specific class)
Definition Details.cpp:48
Wraps a frame of shared_ptr<const EntryInfo> and an index for per entry access. This class manages ow...
Definition Entries.h:555
Represents the default font settings for a particular element type.
Definition CommonClasses.h:111
EnigmaBase class note details. Note details are entry details associated with a note ID.
Definition BaseClasses.h:553
virtual NoteNumber getNoteId() const =0
Required virtual function that returns the note id.
EnigmaBase class for all "options" types.
Definition BaseClasses.h:241
OptionsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode)
Constructs the OptionsBase and validates XmlNodeName in the derived class.
Definition BaseClasses.h:252
Template pattern for OthersBase items consisting of an array of a single item.
Definition BaseClasses.h:335
void integrityCheck(const std::shared_ptr< EnigmaBase > &ptrToThis) override
Override of EnigmaBase::integrityCheck.
Definition BaseClasses.h:358
std::vector< ElementType > values
Definition BaseClasses.h:346
OthersArray(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper cmper)
Constructor function.
Definition BaseClasses.h:341
bool containsValue(const ElementType &value) const noexcept
Returns true if the array contains the specified value.
Definition BaseClasses.h:351
EnigmaBase class for all "others" types.
Definition BaseClasses.h:263
OthersBase & operator=(OthersBase &&other) noexcept
Move assignment preserves the destination DOM instance's document, source and requested part IDs,...
Definition BaseClasses.h:293
OthersBase & operator=(const OthersBase &other)
Copy assignment preserves the destination DOM instance's document, source and requested part IDs,...
Definition BaseClasses.h:284
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:322
std::optional< Inci > getInci() const
Gets the optional array index (inci).
Definition BaseClasses.h:317
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:279
Cmper getCmper() const
Gets the cmper key value.
Definition BaseClasses.h:310
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:377
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:380
std::string name
the name
Definition BaseClasses.h:385
EnigmaBase class for all text blocks.
Definition BaseClasses.h:570
Cmper getTextNumber() const
Returns the raw text number.
Definition BaseClasses.h:617
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:37
TextsBase & operator=(TextsBase &&other) noexcept
Moves the text without changing the destination DOM instance's document, source part,...
Definition BaseClasses.h:602
std::string text
Raw Enigma string (with Enigma string tags), encoded UTF-8.
Definition BaseClasses.h:612
TextsBase(TextsBase &&) noexcept=default
explicit default move constructor
TextsBase(const TextsBase &)=default
explicit default copy constructor
TextsBase(const DocumentWeakPtr &document, Cmper partId, ShareMode shareMode, Cmper textNumber)
Constructs a TextsBase object.
Definition BaseClasses.h:582
void setTextNumber(Cmper textNumber)
Sets the raw text number.
Definition BaseClasses.h:622
Exception for integrity errors. (Used when MUSX_THROW_ON_INTEGRITY_CHECK_FAIL is defined....
Definition BaseClasses.h:50
Wrapper class for interpreting and rendering Enigma-style strings with insert handling.
Definition EnigmaString.h:501
static TextInsertCallback defaultInsertsCallback
Inserts callback to take all default insert subsitutions determined by parseEnigmaText.
Definition EnigmaString.h:413
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:410
std::shared_ptr< const T > MusxInstance
Defines the type of a musx instance stored in a pool.
Definition MusxInstance.h:40
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:81
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:57
std::weak_ptr< Document > DocumentWeakPtr
Shared weak Document pointer.
Definition DocumentElement.h:37
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:71
std::function< void(const EnigmaBase &)> ImportObjectCallback
Receives each document-pool object newly created by an import helper.
Definition BaseClasses.h:150
uint16_t NoteNumber
Note identifier.
Definition Fundamentals.h:72
std::weak_ptr< const T > MusxInstanceWeak
Defines a weak ptr to the type of a musx instance stored in a pool.
Definition MusxInstance.h:45
std::vector< XmlElementDescriptor< T > > XmlElementArray
an array type for XmlElementDescriptor instances.
Definition XmlInterface.h:133
object model for musx file (enigmaxml)
Definition BaseClasses.h:38