MUSX Document Model
Loading...
Searching...
No Matches
ObjectPool.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 <algorithm>
25#include <string>
26#include <map>
27#include <unordered_map>
28#include <vector>
29#include <memory>
30#include <tuple>
31#include <variant>
32#include <stdexcept>
33#include <functional>
34#include <iterator>
35#include <limits>
36#include <typeindex>
37
38#include "MusxInstance.h"
39
40#include "BaseClasses.h"
41#include "Others.h"
42#include "Details.h"
43#include "Entries.h"
44#include "ShapeDesigner.h"
45#include "SmartShape.h"
46#include "Staff.h"
47
48#ifndef DOXYGEN_SHOULD_IGNORE_THIS
49namespace bench {
52template<class> class PoolAccessor;
53}; // namespace bench
54#endif
55
56namespace musx {
57namespace dom {
58
62template <typename Pool, typename T>
63struct is_pool_type : std::false_type {}; // default: not valid
64
65class OptionsPool;
66class OthersPool;
67class DetailsPool;
68class EntryPool;
69class TextsPool;
70
71template <typename T>
72struct is_pool_type<OptionsPool, T> : std::is_base_of<OptionsBase, T> {};
73template <typename T>
74struct is_pool_type<OthersPool, T> : std::is_base_of<OthersBase, T> {};
75template <typename T>
76struct is_pool_type<DetailsPool, T> : std::is_base_of<DetailsBase, T> {};
77template <typename T>
78struct is_pool_type<EntryPool, T> : std::is_same<Entry, T> {};
79template <typename T>
80struct is_pool_type<TextsPool, T> : std::is_base_of<TextsBase, T> {};
81
83template <typename Pool, typename T>
85
95template <typename ObjectBaseType>
97{
98public:
100 using ObjectPtr = std::shared_ptr<ObjectBaseType>;
102 struct ObjectKey {
103 std::type_index typeId;
104 std::string_view nodeId;
106 std::optional<Cmper> cmper1;
107 std::optional<Cmper> cmper2;
108 // Use `int` instead of `Inci` to work around GCC spurious -Wmaybe-uninitialized warning
109 std::optional<int> inci;
110
112 ObjectKey(std::type_index t,
113 std::string_view n,
114 Cmper p,
115 std::optional<Cmper> c1 = std::nullopt,
116 std::optional<Cmper> c2 = std::nullopt,
117 std::optional<int> i = std::nullopt) : typeId(t), nodeId(n), partId(p), cmper1(c1), cmper2(c2), inci(i)
118 {
119 }
120
122 bool operator<(const ObjectKey& other) const
123 {
124 if (typeId != other.typeId) {
125 return typeId < other.typeId;
126 }
127 if (partId != other.partId) {
128 return partId < other.partId;
129 }
130 if (cmper1 != other.cmper1) {
131 return cmper1 < other.cmper1;
132 }
133 if (cmper2 != other.cmper2) {
134 return cmper2 < other.cmper2;
135 }
136 return inci < other.inci;
137 }
138
140 std::string description() const
141 {
142 std::string result = std::string(nodeId) + " part " + std::to_string(partId);
143 if (cmper1) {
144 if (cmper2) {
145 result += " cmpers [" + std::to_string(cmper1.value()) + ", " + std::to_string(cmper2.value()) + "]";
146 } else {
147 result += " cmper " + std::to_string(cmper1.value());
148 }
149 }
150 if (inci) {
151 result += " inci " + std::to_string(inci.value());
152 }
153 return result;
154 }
155 };
156
157 using PoolMap = std::map<ObjectKey, ObjectPtr>;
158
159private:
160 template <typename T>
161 std::shared_ptr<const T> bindWithPartId(std::shared_ptr<const T> obj, Cmper requestedPartId) const
162 {
163 if constexpr (std::is_base_of_v<OthersBase, T> || std::is_base_of_v<DetailsBase, T>) {
164 if (obj && obj->getRequestedPartId() != requestedPartId) {
165 return PartContextCloner::copyWithPartId(obj, requestedPartId);
166 }
167 }
168 return obj;
169 }
170
171 inline static ObjectKey makeEndKey(const ObjectKey& key)
172 {
173 return ObjectKey{
174 key.typeId,
175 key.nodeId,
176 key.partId,
177 key.cmper1.value_or((std::numeric_limits<Cmper>::max)()),
178 key.cmper2.value_or((std::numeric_limits<Cmper>::max)()),
179 key.inci.value_or((std::numeric_limits<Inci>::max)())
180 };
181 }
182
183 template <typename T>
184 inline static MusxInstance<T> checkedStaticCast([[maybe_unused]]const ObjectKey& key, const ObjectPtr& p)
185 {
186 static_assert(std::is_base_of_v<ObjectBaseType, T>, "T must derive from ObjectBaseType");
187 if constexpr (!std::is_same_v<T, ObjectBaseType>) {
188 assert(T::XmlNodeName == key.nodeId && "nodeId/type mismatch: pool invariant violated");
189 }
190 #ifndef NDEBUG
191 // Optional paranoia while refactoring:
192 //assert(std::dynamic_pointer_cast<T>(p));
193 #endif
194 return std::static_pointer_cast<T>(p);
195 }
196
197 inline static bool logicalLess(const ObjectKey& a, const ObjectKey& b)
198 {
199 return std::tie(a.cmper1, a.cmper2, a.inci) < std::tie(b.cmper1, b.cmper2, b.inci);
200 }
201
202 inline static bool logicalEq(const ObjectKey& a, const ObjectKey& b)
203 {
204 return a.cmper1 == b.cmper1 && a.cmper2 == b.cmper2 && a.inci == b.inci;
205 }
206
207public:
209 virtual ~ObjectPool() = default;
210
213 void integrityCheckAll() const
214 {
215 for (const auto& [key, object] : m_pool) {
216 (void)key;
217 object->integrityCheck(object);
218 }
219 }
220
227 void add(ObjectKey key, ObjectPtr object)
228 {
229 if (key.inci.has_value()) {
230 ObjectKey noInciKey = key;
231 noInciKey.inci = std::nullopt;
232 auto currentIncis = getArray<ObjectBaseType>(noInciKey, key.partId);
233 if (key.inci.value() != int(currentIncis.size())) {
234 MUSX_INTEGRITY_ERROR("Node " + std::string(key.nodeId) + " has inci " + std::to_string(key.inci.value()) + " that is out of sequence.");
235 }
236 }
237 auto shareModeIt = m_shareMode.find(key.nodeId);
238 const auto objectShareMode = object->getShareMode();
239 const auto priorSize = m_pool.size();
240 typename PoolMap::iterator poolIt;
241 if (m_insertionHint
242 && (*m_insertionHint)->first.typeId == key.typeId
243 && (*m_insertionHint)->first < key) {
244 poolIt = m_pool.emplace_hint(
245 std::next(*m_insertionHint), std::move(key), std::move(object));
246 } else {
247 poolIt = m_pool.emplace(std::move(key), std::move(object)).first;
248 }
249 const bool emplaced = m_pool.size() != priorSize;
250 m_insertionHint = poolIt;
251 if (!emplaced) {
252 MUSX_INTEGRITY_ERROR("Attempted to add same key more than once: " + poolIt->first.description());
253 }
254 if (shareModeIt == m_shareMode.end()) {
255 m_shareMode.emplace(poolIt->first.nodeId, objectShareMode);
256 } else if (objectShareMode != shareModeIt->second && objectShareMode != EnigmaBase::ShareMode::All) {
257 if (shareModeIt->second == EnigmaBase::ShareMode::All) {
258 m_shareMode[poolIt->first.nodeId] = objectShareMode;
259 } else {
260 MUSX_INTEGRITY_ERROR("Share mode for added " + std::string(poolIt->first.nodeId) + " object [" + std::to_string(int(objectShareMode))
261 + "] does not match previous [" + std::to_string(int(shareModeIt->second)) + "]");
262 }
263 }
264 }
265
279 template <typename T>
280 MusxInstanceList<T> getArray(const ObjectKey& key, Cmper requestedPartId) const
281 {
282 MusxInstanceList<T> result(m_document, requestedPartId);
283
284 auto rangeStart = m_pool.lower_bound(key);
285 auto rangeEnd = m_pool.upper_bound(makeEndKey(key));
286
287 for (auto it = rangeStart; it != rangeEnd; ++it) {
288 auto typedPtr = bindWithPartId<T>(checkedStaticCast<T>(key, it->second), requestedPartId);
289 result.push_back(typedPtr);
290 }
291 return result;
292 }
293
306 template <typename T>
308 {
310 if (key.partId != SCORE_PARTID) {
311 auto it = m_shareMode.find(key.nodeId);
312 // If the nodeId is not found in m_shareMode, it means the document contains no instances,
313 // and every path will return an empty vector. We can safely ignore this case.
314 if (it != m_shareMode.end()) {
315 forShareMode = it->second;
316 }
317 }
318
319 if (key.partId == SCORE_PARTID || forShareMode == EnigmaBase::ShareMode::None) {
320 return getArray<T>(key, key.partId);
321 }
322 ObjectKey scoreKey(key);
323 scoreKey.partId = SCORE_PARTID;
324 if (forShareMode == EnigmaBase::ShareMode::All) {
325 return getArray<T>(scoreKey, key.partId);
326 }
327
328 const auto partStart = m_pool.lower_bound(key);
329 const auto partEnd = m_pool.upper_bound(makeEndKey(key));
330
331 const auto scoreStart = m_pool.lower_bound(scoreKey);
332 const auto scoreEnd = m_pool.upper_bound(makeEndKey(scoreKey));
333
334 auto partIt = partStart;
335 auto scoreIt = scoreStart;
336
337 MusxInstanceList<T> result(m_document, key.partId);
338 auto pushInstance = [&](const auto& it) {
339 auto typed = bindWithPartId<T>(checkedStaticCast<T>(key, it->second), key.partId);
340 result.push_back(typed);
341 };
342 while (partIt != partEnd || scoreIt != scoreEnd) {
343 if (scoreIt == scoreEnd) {
344 pushInstance(partIt++);
345 continue;
346 }
347 if (partIt == partEnd) {
348 pushInstance(scoreIt++); // bind score record to requested part
349 continue;
350 }
351 const ObjectKey& pk = partIt->first;
352 const ObjectKey& sk = scoreIt->first;
353 if (logicalEq(pk, sk)) {
354 pushInstance(partIt++); // prefer part instance
355 scoreIt++;
356 } else if (logicalLess(pk, sk)) {
357 pushInstance(partIt++);
358 } else {
359 pushInstance(scoreIt++); // score fallback
360 }
361 }
362 return result;
363 }
364
376 template <typename T>
378 {
379 auto it = m_pool.find(key);
380 if (it == m_pool.end()) {
381 return nullptr;
382 }
383 return checkedStaticCast<T>(key, it->second);
384 }
385
393 template <typename T>
394 std::vector<MusxInstance<T>> getAllSources(
395 std::optional<Cmper> cmper1 = std::nullopt,
396 std::optional<Cmper> cmper2 = std::nullopt,
397 std::optional<Inci> inci = std::nullopt) const
398 {
399 const ObjectKey lowerKey{std::type_index(typeid(T)), T::XmlNodeName, SCORE_PARTID, cmper1, cmper2, inci};
400 const auto upperKey = [&]() -> ObjectKey {
401 auto result = makeEndKey(lowerKey);
402 result.partId = (std::numeric_limits<Cmper>::max)();
403 return result;
404 }();
405 std::vector<MusxInstance<T>> result;
406 for (auto it = m_pool.lower_bound(lowerKey); it != m_pool.upper_bound(upperKey); ++it) {
407 const auto& key = it->first;
408 if (cmper1 && key.cmper1 != cmper1) continue;
409 if (cmper2 && key.cmper2 != cmper2) continue;
410 if (inci && key.inci != inci) continue;
411 result.push_back(checkedStaticCast<T>(key, it->second));
412 }
413 return result;
414 }
415
429 template <typename T>
431 {
432 if (auto partVersion = getSource<T>(key)) {
433 return partVersion;
434 }
435 if (key.partId == SCORE_PARTID) {
436 // if this is already the score version, there is nothing to return.
437 return nullptr;
438 } else {
439 // if this is for a part, do not search score for unshared types.
440 auto it = m_shareMode.find(key.nodeId);
441 if (it != m_shareMode.end() && it->second == EnigmaBase::ShareMode::None) {
442 return nullptr;
443 }
444 }
445 ObjectKey scoreKey(key);
446 scoreKey.partId = SCORE_PARTID;
447 return getSource<T>(scoreKey);
448 }
449
463 template <typename T>
465 {
466 return bindWithPartId<T>(getEffectiveSourceForPart<T>(key), key.partId);
467 }
468
475 template <typename T, typename IdGetter>
476 static std::optional<Cmper> nextFreeCmper(
477 const MusxInstanceList<T>& items, IdGetter getId)
478 {
479 Cmper highest = 0;
480 for (const auto& item : items) {
481 highest = (std::max)(highest, std::invoke(getId, *item));
482 }
483 if (highest == (std::numeric_limits<Cmper>::max)()) {
484 return std::nullopt;
485 }
486 return Cmper(highest + 1);
487 }
488
493 ObjectPool(const DocumentWeakPtr& document, const std::unordered_map<std::string_view, dom::EnigmaBase::ShareMode>& knownShareModes = {})
494 : m_document(document), m_shareMode(knownShareModes) {}
495
497 ObjectPool(const ObjectPool& other)
498 : m_document(other.m_document), m_shareMode(other.m_shareMode), m_pool(other.m_pool) {}
499
502 {
503 if (this != &other) {
504 m_document = other.m_document;
505 m_shareMode = other.m_shareMode;
506 m_pool = other.m_pool;
507 m_insertionHint.reset();
508 }
509 return *this;
510 }
511
512 ObjectPool(ObjectPool&&) = default;
513 ObjectPool& operator=(ObjectPool&&) = default;
514
515private:
516 DocumentWeakPtr m_document;
517 std::unordered_map<std::string_view, dom::EnigmaBase::ShareMode> m_shareMode;
518
519 PoolMap m_pool;
520 std::optional<typename PoolMap::iterator> m_insertionHint;
521};
522
528{
530 friend class bench::PoolAccessor<OptionsPool>;
531
532public:
534 OptionsPool(const DocumentWeakPtr& document) : m_pool(document) {}
535
537 void add(std::string_view nodeName, std::shared_ptr<OptionsBase> instance)
538 {
539 const EnigmaBase* basePtr = instance.get();
540 if (basePtr->getSourcePartId() != SCORE_PARTID) {
541 MUSX_INTEGRITY_ERROR("Options node " + std::string(nodeName) + " hase non-score part id [" + std::to_string(basePtr->getSourcePartId()) + "]");
542 }
544 std::type_index(typeid(*basePtr)), nodeName, basePtr->getSourcePartId()};
545 m_pool.add(key, std::move(instance));
546 }
547
549 void integrityCheckAll() const { m_pool.integrityCheckAll(); }
550
552 template <typename T>
554 {
555 static_assert(is_pool_type_v<OptionsPool, T>, "Type T is not registered in OptionsPool");
556 return m_pool.getArray<T>({ std::type_index(typeid(T)), T::XmlNodeName,
558 }
559
561 template <typename T>
563 {
564 static_assert(is_pool_type_v<OptionsPool, T>, "Type T is not registered in OptionsPool");
565 return m_pool.getSource<T>({ std::type_index(typeid(T)), T::XmlNodeName,
566 SCORE_PARTID });
567 }
568};
570using OptionsPoolPtr = std::shared_ptr<OptionsPool>;
571
577{
579 friend class bench::PoolAccessor<OthersPool>;
580 friend MusxInstance<others::StaffComposite> others::StaffComposite::createCurrent(const DocumentPtr& document, Cmper partId, StaffCmper staffId, MeasCmper measId, Edu eduPosition);
581
582 MusxInstance<others::Staff> getRawStaff(Cmper partId, StaffCmper staffId)
583 {
585 std::type_index(typeid(others::Staff)), others::Staff::XmlNodeName,
586 partId, staffId, std::nullopt, std::nullopt });
587 }
588
589public:
604
606 void add(std::string_view nodeName, std::shared_ptr<OthersBase> instance)
607 {
608 const OthersBase* instancePtr = instance.get();
610 std::type_index(typeid(*instancePtr)), nodeName,
611 instance->getSourcePartId(), instance->getCmper(),
612 std::nullopt, instance->getInci()};
613 m_pool.add(key, std::move(instance));
614 }
615
617 void integrityCheckAll() const { m_pool.integrityCheckAll(); }
618
620 template <typename T>
621 MusxInstanceList<T> getArray(Cmper partId, std::optional<Cmper> cmper = std::nullopt) const
622 {
623 static_assert(is_pool_type_v<OthersPool, T>, "Type T is not registered in OthersPool");
624 return m_pool.getArrayForPart<T>({ std::type_index(typeid(T)),
625 T::XmlNodeName, partId, cmper });
626 }
627
629 template <typename T>
630 MusxInstance<T> get(Cmper partId, Cmper cmper, std::optional<Inci> inci = std::nullopt) const
631 {
632 static_assert(is_pool_type_v<OthersPool, T>, "Type T is not registered in OthersPool");
633 return m_pool.getEffectiveForPart<T>({ std::type_index(typeid(T)),
634 T::XmlNodeName, partId, cmper, std::nullopt, inci });
635 }
636
638 template <typename T>
639 std::vector<MusxInstance<T>> getAllSources() const
640 {
641 static_assert(is_pool_type_v<OthersPool, T>, "Type T is not registered in OthersPool");
642 return m_pool.template getAllSources<T>();
643 }
644
651 template <typename T>
652 std::vector<MusxInstance<T>> getAllSources(
653 Cmper cmper, std::optional<Inci> inci = std::nullopt) const
654 {
655 static_assert(is_pool_type_v<OthersPool, T>, "Type T is not registered in OthersPool");
656 return m_pool.template getAllSources<T>(cmper, std::nullopt, inci);
657 }
658
666 template <typename T>
667 std::optional<Cmper> nextFreeCmper(Cmper partId) const
668 {
669 static_assert(is_pool_type_v<OthersPool, T>, "Type T is not registered in OthersPool");
670 return m_pool.nextFreeCmper(getArray<T>(partId), &OthersBase::getCmper);
671 }
672};
674using OthersPoolPtr = std::shared_ptr<OthersPool>;
675
683{
685 friend class bench::PoolAccessor<DetailsPool>;
686
687public:
689 DetailsPool(const DocumentWeakPtr& document) : m_pool(document, {
692 // add other known sharemode none items as they are identified.
693 }) {}
694
696 void add(std::string_view nodeName, std::shared_ptr<DetailsBase> instance)
697 {
698 const DetailsBase* instancePtr = instance.get();
700 std::type_index(typeid(*instancePtr)), nodeName,
701 instance->getSourcePartId(), instance->getCmper1(),
702 instance->getCmper2(), instance->getInci()};
703 m_pool.add(key, std::move(instance));
704 }
705
707 void integrityCheckAll() const { m_pool.integrityCheckAll(); }
708
710 template <typename T, typename = std::enable_if_t<is_pool_type_v<DetailsPool, T>>>
712 { return m_pool.template getArrayForPart<T>({ std::type_index(typeid(T)), T::XmlNodeName, partId }); }
713
715 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
716 MusxInstanceList<T> getArray(Cmper partId, Cmper cmper1, std::optional<Cmper> cmper2 = std::nullopt) const
717 {
718 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
719 return m_pool.template getArrayForPart<T>({ std::type_index(typeid(T)),
720 T::XmlNodeName, partId, cmper1, cmper2 });
721 }
722
724 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
726 {
727 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
728 return m_pool.template getArrayForPart<T>({ std::type_index(typeid(T)),
729 T::XmlNodeName, partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff) });
730 }
731
733 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
734 MusxInstance<T> get(Cmper partId, Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt) const
735 {
736 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
737 return m_pool.getEffectiveForPart<T>({ std::type_index(typeid(T)),
738 T::XmlNodeName, partId, cmper1, cmper2, inci });
739 }
740
742 template <typename T>
743 std::vector<MusxInstance<T>> getAllSources() const
744 {
745 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
746 return m_pool.template getAllSources<T>();
747 }
748
756 template <typename T>
757 std::vector<MusxInstance<T>> getAllSources(
758 Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt) const
759 {
760 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
761 return m_pool.template getAllSources<T>(cmper1, cmper2, inci);
762 }
763
765 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
766 MusxInstance<T> get(Cmper partId, EntryNumber entnum, std::optional<Inci> inci = std::nullopt) const
767 {
768 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
769 return m_pool.getEffectiveForPart<T>({ std::type_index(typeid(T)),
770 T::XmlNodeName, partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff), inci });
771 }
772
779 template <typename T, typename std::enable_if_t<std::is_base_of_v<NoteDetailsBase, T>, int> = 0>
780 MusxInstance<T> getForNote(const NoteInfoPtr& noteInfo, const std::optional<Cmper>& forPartId = std::nullopt)
781 {
782 static_assert(is_pool_type_v<DetailsPool, T>, "Type T is not registered in DetailsPool");
783 auto details = getArray<T>(
784 forPartId.value_or(noteInfo.getEntryInfo().getFrame()->getRequestedPartId()),
785 noteInfo.getEntryInfo()->getEntry()->getEntryNumber()
786 );
787 for (const auto& detail : details) {
788 if (detail->getNoteId() == noteInfo->getNoteId()) {
789 return detail;
790 }
791 }
792 return nullptr;
793 }
794};
795
797using DetailsPoolPtr = std::shared_ptr<DetailsPool>;
798
800class EntryPool // uses different implementation than other pools for more efficient access
801{
802public:
804 EntryPool(const DocumentWeakPtr& document) : m_document(document) {}
805
807 bool empty() const { return m_pool.empty(); }
808
810 void add(EntryNumber entryNumber, std::shared_ptr<Entry> instance)
811 {
812 auto [it, emplaced] = m_pool.emplace(entryNumber, std::move(instance));
813 if (!emplaced) {
814 MUSX_INTEGRITY_ERROR("Entry number " + std::to_string(entryNumber) + " added twice.");
815 }
816 }
817
819 void integrityCheckAll() const
820 {
821 std::vector<EntryNumber> entryNumbers;
822 entryNumbers.reserve(m_pool.size());
823 for (const auto& [entryNumber, entry] : m_pool) {
824 (void)entry;
825 entryNumbers.push_back(entryNumber);
826 }
827 std::sort(entryNumbers.begin(), entryNumbers.end());
828 for (const auto entryNumber : entryNumbers) {
829 const auto& entry = m_pool.at(entryNumber);
830 entry->integrityCheck(entry);
831 }
832 }
833
836 {
837 const auto it = m_pool.find(entryNumber);
838 if (it == m_pool.end()) {
839 return nullptr;
840 }
841 return it->second;
842 }
843
844private:
845 DocumentWeakPtr m_document;
846 std::unordered_map<EntryNumber, std::shared_ptr<Entry>> m_pool;
847
848 friend class bench::PoolAccessor<EntryPool>;
849};
851using EntryPoolPtr = std::shared_ptr<EntryPool>;
852
855{
857 friend class bench::PoolAccessor<TextsPool>;
858
859public:
861 TextsPool(const DocumentWeakPtr& document) : m_pool(document) {}
862
864 void add(std::string_view nodeName, std::shared_ptr<TextsBase> instance)
865 {
866 const EnigmaBase* basePtr = instance.get();
867 if (basePtr->getSourcePartId() != SCORE_PARTID) {
868 MUSX_INTEGRITY_ERROR("Texts node " + std::string(nodeName) + " hase non-score part id [" + std::to_string(basePtr->getSourcePartId()) + "]");
869 }
870 const TextsBase* instancePtr = instance.get();
872 std::type_index(typeid(*instancePtr)), nodeName,
873 basePtr->getSourcePartId(), instance->getTextNumber()};
874 m_pool.add(key, std::move(instance));
875 }
876
878 void integrityCheckAll() const { m_pool.integrityCheckAll(); }
879
881 template <typename T>
882 MusxInstanceList<T> getArray(std::optional<Cmper> cmper = std::nullopt) const
883 {
884 static_assert(is_pool_type_v<TextsPool, T>, "Type T is not registered in TextsPool");
885 return m_pool.getArray<T>({ std::type_index(typeid(T)), T::XmlNodeName,
886 SCORE_PARTID, cmper }, SCORE_PARTID);
887 }
888
890 template <typename T>
892 {
893 static_assert(is_pool_type_v<TextsPool, T>, "Type T is not registered in TextsPool");
894 return m_pool.getSource<T>({ std::type_index(typeid(T)), T::XmlNodeName,
895 SCORE_PARTID, cmper, std::nullopt, std::nullopt });
896 }
897
906 template <typename T>
907 std::optional<Cmper> nextFreeCmper() const
908 {
909 static_assert(is_pool_type_v<TextsPool, T>, "Type T is not registered in TextsPool");
910 return m_pool.nextFreeCmper(getArray<T>(), &TextsBase::getTextNumber);
911 }
912};
914using TextsPoolPtr = std::shared_ptr<TextsPool>;
915
916} // namespace dom
917} // namespace musx
EnigmaBase class for all "details" types.
Definition BaseClasses.h:393
A pool that manages collections of DetailsBase objects, organized by XML node names and Cmper values.
Definition ObjectPool.h:683
MusxInstanceList< T > getArray(Cmper partId) const
version of ObjectPool::getArray for getting all of them
Definition ObjectPool.h:711
std::vector< MusxInstance< T > > getAllSources(Cmper cmper1, Cmper cmper2, std::optional< Inci > inci=std::nullopt) const
Returns physically stored source objects for one comparator pair and optional incidence.
Definition ObjectPool.h:757
MusxInstance< T > getForNote(const NoteInfoPtr &noteInfo, const std::optional< Cmper > &forPartId=std::nullopt)
Returns the detail for a particular note.
Definition ObjectPool.h:780
void add(std::string_view nodeName, std::shared_ptr< DetailsBase > instance)
DetailsPool version of ObjectPool::add.
Definition ObjectPool.h:696
DetailsPool(const DocumentWeakPtr &document)
Constructor.
Definition ObjectPool.h:689
MusxInstance< T > get(Cmper partId, EntryNumber entnum, std::optional< Inci > inci=std::nullopt) const
Get a single EntryDetailsBase item out of the pool.
Definition ObjectPool.h:766
std::vector< MusxInstance< T > > getAllSources() const
Returns every physically stored source object of type T.
Definition ObjectPool.h:743
MusxInstanceList< T > getArray(Cmper partId, EntryNumber entnum) const
EntryDetailsPool version of ObjectPool::getArray.
Definition ObjectPool.h:725
MusxInstance< T > get(Cmper partId, Cmper cmper1, Cmper cmper2, std::optional< Inci > inci=std::nullopt) const
Get a single DetailsBase item out of the pool (not EntryDetailsBase)
Definition ObjectPool.h:734
void integrityCheckAll() const
Validates every details object. Called by document construction finalization.
Definition ObjectPool.h:707
MusxInstanceList< T > getArray(Cmper partId, Cmper cmper1, std::optional< Cmper > cmper2=std::nullopt) const
DetailsPool version of ObjectPool::getArray.
Definition ObjectPool.h:716
Base for DOM classes that are represented in EnigmaData.
Definition BaseClasses.h:88
ShareMode
Describes how this instance is shared between part and score.
Definition BaseClasses.h:95
Cmper getSourcePartId() const
Gets the source partId for this instance. If an instance is fully shared with the score,...
Definition BaseClasses.h:108
std::shared_ptr< const EntryFrame > getFrame() const
Returns the frame.
Definition Entries.h:656
Entry pool.
Definition ObjectPool.h:801
EntryPool(const DocumentWeakPtr &document)
Constructor function.
Definition ObjectPool.h:804
void integrityCheckAll() const
Validates every entry in entry-number order.
Definition ObjectPool.h:819
void add(EntryNumber entryNumber, std::shared_ptr< Entry > instance)
Add an entry to the EntryPool. (Used by the factory.)
Definition ObjectPool.h:810
bool empty() const
Returns whether the pool contains no entries.
Definition ObjectPool.h:807
MusxInstance< Entry > get(EntryNumber entryNumber) const
Get an entry from the EntryPool.
Definition ObjectPool.h:835
Provides optional per-type extension methods for MusxInstanceList.
Definition MusxInstance.h:118
Wraps an EntryInfo instance and a note index.
Definition Entries.h:1619
EntryInfoPtr getEntryInfo() const
Gets the entry info for this note.
Definition Entries.h:1659
A pool that manages collections of OthersBase objects, organized by XML node names and Cmper values.
Definition ObjectPool.h:97
ObjectPool(const DocumentWeakPtr &document, const std::unordered_map< std::string_view, dom::EnigmaBase::ShareMode > &knownShareModes={})
Constructs the object pool.
Definition ObjectPool.h:493
virtual ~ObjectPool()=default
virtual destructor
static std::optional< Cmper > nextFreeCmper(const MusxInstanceList< T > &items, IdGetter getId)
Returns the identifier one past the highest currently used in items.
Definition ObjectPool.h:476
ObjectPool(const ObjectPool &other)
Copies the stored objects without retaining an iterator into the source pool.
Definition ObjectPool.h:497
MusxInstance< T > getSource(const ObjectKey &key) const
Retrieves the first (and usually only) object of a specific type from the pool.
Definition ObjectPool.h:377
MusxInstanceList< T > getArrayForPart(const ObjectKey &key) const
Retrieves a vector of objects of a specific type from the pool.
Definition ObjectPool.h:307
MusxInstanceList< T > getArray(const ObjectKey &key, Cmper requestedPartId) const
Retrieves a vector of objects of a specific type from the pool.
Definition ObjectPool.h:280
std::vector< MusxInstance< T > > getAllSources(std::optional< Cmper > cmper1=std::nullopt, std::optional< Cmper > cmper2=std::nullopt, std::optional< Inci > inci=std::nullopt) const
Returns physically stored objects of one type across every source part.
Definition ObjectPool.h:394
ObjectPool & operator=(const ObjectPool &other)
Copies the stored objects without retaining an iterator into the source pool.
Definition ObjectPool.h:501
MusxInstance< T > getEffectiveSourceForPart(const ObjectKey &key) const
Retrieves the first (and usually only) object of a specific type from the pool for a part.
Definition ObjectPool.h:430
void add(ObjectKey key, ObjectPtr object)
Adds an ObjectBaseType object to the pool.
Definition ObjectPool.h:227
MusxInstance< T > getEffectiveForPart(const ObjectKey &key) const
Retrieves the first (and usually only) object of a specific type from the pool for a part.
Definition ObjectPool.h:464
std::shared_ptr< ObjectBaseType > ObjectPtr
shared pointer to ObjectBaseType
Definition ObjectPool.h:100
void integrityCheckAll() const
Runs object-local integrity validation for every stored source object.
Definition ObjectPool.h:213
A pool that manages collections of OptionsBase objects that have no Cmper value.
Definition ObjectPool.h:528
void integrityCheckAll() const
Validates every option object. Called by document construction finalization.
Definition ObjectPool.h:549
MusxInstance< T > get() const
Get a single item out of the pool.
Definition ObjectPool.h:562
MusxInstanceList< T > getArray() const
Scalar version of ObjectPool::getArray.
Definition ObjectPool.h:553
void add(std::string_view nodeName, std::shared_ptr< OptionsBase > instance)
Scalar version of ObjectPool::add.
Definition ObjectPool.h:537
OptionsPool(const DocumentWeakPtr &document)
Constructor function.
Definition ObjectPool.h:534
EnigmaBase class for all "others" types.
Definition BaseClasses.h:261
Cmper getCmper() const
Gets the cmper key value.
Definition BaseClasses.h:306
A pool that manages collections of OthersBase objects.
Definition ObjectPool.h:577
std::vector< MusxInstance< T > > getAllSources() const
Returns every physically stored source object of type T.
Definition ObjectPool.h:639
void add(std::string_view nodeName, std::shared_ptr< OthersBase > instance)
OthersPool version of ObjectPool::add.
Definition ObjectPool.h:606
void integrityCheckAll() const
Validates every others object. Called by document construction finalization.
Definition ObjectPool.h:617
MusxInstance< T > get(Cmper partId, Cmper cmper, std::optional< Inci > inci=std::nullopt) const
Get a single item out of the pool.
Definition ObjectPool.h:630
std::optional< Cmper > nextFreeCmper(Cmper partId) const
The cmper one past the highest currently used for T.
Definition ObjectPool.h:667
OthersPool(const DocumentWeakPtr &document)
Constructor.
Definition ObjectPool.h:591
std::vector< MusxInstance< T > > getAllSources(Cmper cmper, std::optional< Inci > inci=std::nullopt) const
Returns physically stored source objects for one comparator and optional incidence.
Definition ObjectPool.h:652
MusxInstanceList< T > getArray(Cmper partId, std::optional< Cmper > cmper=std::nullopt) const
OthersPool version of ObjectPool::getArray.
Definition ObjectPool.h:621
EnigmaBase class for all text blocks.
Definition BaseClasses.h:564
Cmper getTextNumber() const
Returns the raw text number.
Definition BaseClasses.h:584
Text pool.
Definition ObjectPool.h:855
MusxInstanceList< T > getArray(std::optional< Cmper > cmper=std::nullopt) const
Texts version of ObjectPool::getArray.
Definition ObjectPool.h:882
TextsPool(const DocumentWeakPtr &document)
Constructor fundtion.
Definition ObjectPool.h:861
MusxInstance< T > get(Cmper cmper) const
Get a single item out of the pool.
Definition ObjectPool.h:891
void add(std::string_view nodeName, std::shared_ptr< TextsBase > instance)
Texts version of ObjectPool::add.
Definition ObjectPool.h:864
void integrityCheckAll() const
Validates every text object. Called by document construction finalization.
Definition ObjectPool.h:878
std::optional< Cmper > nextFreeCmper() const
The text number one past the highest currently used for T.
Definition ObjectPool.h:907
static constexpr std::string_view XmlNodeName
XML node name for this type.
Definition Details.h:1941
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Details.h:2021
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:415
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1736
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1718
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1892
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2136
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2202
static MusxInstance< StaffComposite > createCurrent(const DocumentPtr &document, Cmper partId, StaffCmper staffId, MeasCmper measId, Edu eduPosition)
Calculates the current staff at the specified metric position by applying all relevant staff styles,...
Definition Staff.cpp:1223
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Staff.h:742
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2897
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2930
Represents the definition of a Finale staff.
Definition Staff.h:54
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Staff.h:559
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2955
std::shared_ptr< OthersPool > OthersPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:674
int16_t MeasCmper
Enigma meas Cmper (may be negative when not applicable)
Definition Fundamentals.h:66
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
constexpr bool is_pool_type_v
Value shortcut for is_pool_type.
Definition ObjectPool.h:84
std::shared_ptr< DetailsPool > DetailsPoolPtr
Shared DetailsPool pointer.
Definition ObjectPool.h:797
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:57
std::shared_ptr< TextsPool > TextsPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:914
int32_t Edu
"Enigma Durational Units" value (1024 per quarter note)
Definition Fundamentals.h:63
std::shared_ptr< OptionsPool > OptionsPoolPtr
Shared OptionsPool pointer.
Definition ObjectPool.h:570
std::weak_ptr< Document > DocumentWeakPtr
Shared weak Document pointer.
Definition DocumentElement.h:37
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:71
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition DocumentElement.h:35
int16_t StaffCmper
Enigma staff (staffId) Cmper (may be negative when not applicable)
Definition Fundamentals.h:67
std::shared_ptr< EntryPool > EntryPoolPtr
Shared EntryPool pointer.
Definition ObjectPool.h:851
object model for musx file (enigmaxml)
Definition BaseClasses.h:38
key type for storing in pool
Definition ObjectPool.h:102
std::optional< Cmper > cmper2
optional cmper2 for Details.
Definition ObjectPool.h:107
std::optional< Cmper > cmper1
optional cmper1 for Others, Texts, Details.
Definition ObjectPool.h:106
Cmper partId
the part this item is associated with (or 0 for score).
Definition ObjectPool.h:105
std::type_index typeId
runtime identity of the object's concrete class.
Definition ObjectPool.h:103
ObjectKey(std::type_index t, std::string_view n, Cmper p, std::optional< Cmper > c1=std::nullopt, std::optional< Cmper > c2=std::nullopt, std::optional< int > i=std::nullopt)
explicit constructor for optional parameters
Definition ObjectPool.h:112
bool operator<(const ObjectKey &other) const
comparison operator for std::map
Definition ObjectPool.h:122
std::string_view nodeId
the identifier for this node. usually the XML node name.
Definition ObjectPool.h:104
std::optional< int > inci
optional inci for multi-inci classes
Definition ObjectPool.h:109
std::string description() const
provides a description of the key for diagnostic purposes
Definition ObjectPool.h:140
Type trait to determine if a class in a given pool.
Definition ObjectPool.h:63