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 <string>
25#include <map>
26#include <unordered_map>
27#include <vector>
28#include <memory>
29#include <tuple>
30#include <variant>
31#include <stdexcept>
32#include <functional>
33#include <limits>
34
35#include "BaseClasses.h"
36#include "Others.h"
37#include "Details.h"
38#include "Entries.h"
39#include "SmartShape.h"
40
41namespace musx {
42namespace dom {
43
53template <typename ObjectBaseType>
55{
56public:
58 using ObjectPtr = std::shared_ptr<ObjectBaseType>;
60 struct ObjectKey {
61 std::string nodeId;
63 std::optional<Cmper> cmper1;
64 std::optional<Cmper> cmper2;
65 std::optional<Inci> inci;
66
68 ObjectKey(const std::string n,
69 Cmper p,
70 std::optional<Cmper> c1 = std::nullopt,
71 std::optional<Cmper> c2 = std::nullopt,
72 std::optional<Inci> i = std::nullopt) : nodeId(n), partId(p), cmper1(c1), cmper2(c2), inci(i)
73 {
74 }
75
77 bool operator<(const ObjectKey& other) const
78 {
79 if (nodeId != other.nodeId) {
80 return nodeId < other.nodeId;
81 }
82 if (partId != other.partId) {
83 return partId < other.partId;
84 }
85 if (cmper1 != other.cmper1) {
86 return cmper1 < other.cmper1;
87 }
88 if (cmper2 != other.cmper2) {
89 return cmper2 < other.cmper2;
90 }
91 return inci < other.inci;
92 }
93 };
94
96 virtual ~ObjectPool() = default;
97
104 void add(const ObjectKey& key, ObjectPtr object)
105 {
106 if (key.inci.has_value()) {
107 ObjectKey noInciKey = key;
108 noInciKey.inci = std::nullopt;
109 auto currentIncis = getArray<ObjectBaseType>(noInciKey);
110 if (key.inci.value() != int(currentIncis.size())) {
111 MUSX_INTEGRITY_ERROR("Node " + key.nodeId + " has inci " + std::to_string(key.inci.value()) + " that is out of sequence.");
112 }
113 }
114 m_pool.emplace(key, object);
115 auto it = m_shareMode.find(key.nodeId);
116 if (it == m_shareMode.end()) {
117 m_shareMode.emplace(key.nodeId, object->getShareMode());
118 } else if (object->getShareMode() != it->second && object->getShareMode() != Base::ShareMode::All) {
119 if (it->second == Base::ShareMode::All) {
120 m_shareMode[key.nodeId] = object->getShareMode();
121 } else {
122 MUSX_INTEGRITY_ERROR("Share mode for added " + key.nodeId + " object [" + std::to_string(int(object->getShareMode()))
123 + "] does not match previous [" + std::to_string(int(it->second)) + "]");
124 }
125 }
126 }
127
140 template <typename T>
141 std::vector<std::shared_ptr<T>> getArray(const ObjectKey& key) const
142 {
143 std::vector<std::shared_ptr<T>> result;
144
145 auto rangeStart = m_pool.lower_bound(key);
146 auto rangeEnd = m_pool.upper_bound(
147 ObjectKey{
148 key.nodeId,
149 key.partId,
150 key.cmper1.value_or((std::numeric_limits<Cmper>::max)()),
151 key.cmper2.value_or((std::numeric_limits<Cmper>::max)()),
152 key.inci.value_or((std::numeric_limits<Inci>::max)())
153 }
154 );
155
156 for (auto it = rangeStart; it != rangeEnd; ++it) {
157 auto typedPtr = std::dynamic_pointer_cast<T>(it->second);
158 assert(typedPtr);
159 result.push_back(typedPtr);
160 }
161 return result;
162 }
163
176 template <typename T>
177 std::vector<std::shared_ptr<T>> getArrayForPart(const ObjectKey& key) const
178 {
180 if (key.partId != SCORE_PARTID) {
181 auto it = m_shareMode.find(key.nodeId);
182 // If the nodeId is not found in m_shareMode, it means the document contains no instances,
183 // and every path will return an empty vector. We can safely ignore this case.
184 if (it != m_shareMode.end()) {
185 forShareMode = it->second;
186 }
187 if (forShareMode == Base::ShareMode::Partial) {
188 if constexpr (std::is_base_of_v<OthersBase, T>) {
189 if (!key.cmper1.has_value()) {
190 throw std::invalid_argument("Array searches on partially shared Others must supply a cmper.");
191 }
192 } else if constexpr (std::is_base_of_v<DetailsBase, T>) {
193 if (!key.cmper1.has_value() || !key.cmper2.has_value()) {
194 throw std::invalid_argument("Array searches on partially shared Details must supply both cmpers.");
195 }
196 }
197 }
198 }
199 auto keyResult = getArray<T>(key);
200 if (!keyResult.empty() || key.partId == SCORE_PARTID || forShareMode == Base::ShareMode::None) {
201 return keyResult;
202 }
203 ObjectKey scoreKey(key);
204 scoreKey.partId = SCORE_PARTID;
205 return getArray<T>(scoreKey);
206 }
207
219 template <typename T>
220 std::shared_ptr<T> get(const ObjectKey& key) const
221 {
222 auto it = m_pool.find(key);
223 if (it == m_pool.end()) {
224 return nullptr;
225 }
226 auto typedPtr = std::dynamic_pointer_cast<T>(it->second);
227 assert(typedPtr); // There is a program bug if the pointer cast fails.
228 return typedPtr;
229 }
230
241 template <typename T>
242 std::shared_ptr<T> getEffectiveForPart(const ObjectKey& key) const
243 {
244 if (auto partVersion = get<T>(key)) {
245 return partVersion;
246 }
247 if (key.partId == SCORE_PARTID) {
248 // if this is already the score version, there is nothing to return.
249 return nullptr;
250 }
251 ObjectKey scoreKey(key);
252 scoreKey.partId = SCORE_PARTID;
253 return get<T>(scoreKey);
254 }
255
256protected:
260 ObjectPool(const std::unordered_map<std::string, dom::Base::ShareMode>& knownShareModes = {})
261 : m_shareMode(knownShareModes) {}
262
263private:
264 std::map<ObjectKey, ObjectPtr> m_pool;
265 std::unordered_map<std::string, dom::Base::ShareMode> m_shareMode;
266};
267
272class OptionsPool : protected ObjectPool<OptionsBase>
273{
274public:
276 void add(const std::string& nodeName, const std::shared_ptr<OptionsBase>& instance)
277 {
278 if (instance->getPartId()) {
279 MUSX_INTEGRITY_ERROR("Options node " + nodeName + " hase non-zero part id [" + std::to_string(instance->getPartId()) + "]");
280 }
281 ObjectPool::add({ nodeName, instance->getPartId() }, instance);
282 }
283
285 template <typename T>
286 std::vector<std::shared_ptr<T>> getArray() const
287 {
288 return ObjectPool::getArray<T>({ std::string(T::XmlNodeName), SCORE_PARTID });
289 }
290
292 template <typename T>
293 std::shared_ptr<T> get() const
294 {
295 return ObjectPool::get<T>({ std::string(T::XmlNodeName), SCORE_PARTID });
296 }
297};
299using OptionsPoolPtr = std::shared_ptr<OptionsPool>;
300
305class OthersPool : public ObjectPool<OthersBase>
306{
307public:
322
324 void add(const std::string& nodeName, const std::shared_ptr<OthersBase>& instance)
325 { ObjectPool::add({nodeName, instance->getPartId(), instance->getCmper(), std::nullopt, instance->getInci()}, instance); }
326
328 template <typename T>
329 std::vector<std::shared_ptr<T>> getArray(Cmper partId, std::optional<Cmper> cmper = std::nullopt) const
330 { return ObjectPool::getArrayForPart<T>({ std::string(T::XmlNodeName), partId, cmper }); }
331
333 template <typename T>
334 std::shared_ptr<T> get(Cmper partId, Cmper cmper, std::optional<Inci> inci = std::nullopt) const
335 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, cmper, std::nullopt, inci}); }
336};
338using OthersPoolPtr = std::shared_ptr<OthersPool>;
339
346class DetailsPool : protected ObjectPool<DetailsBase>
347{
348public:
354 // add other known sharemode none items as they are identified.
355 }) {}
356
358 void add(const std::string& nodeName, const std::shared_ptr<DetailsBase>& instance)
359 { ObjectPool::add({nodeName, instance->getPartId(), instance->getCmper1(), instance->getCmper2(), instance->getInci()}, instance); }
360
362 template <typename T>
363 std::vector<std::shared_ptr<T>> getArray(Cmper partId) const
364 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId }); }
365
367 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
368 std::vector<std::shared_ptr<T>> getArray(Cmper partId, Cmper cmper1, std::optional<Cmper> cmper2 = std::nullopt) const
369 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId, cmper1, cmper2 }); }
370
372 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
373 std::vector<std::shared_ptr<T>> getArray(Cmper partId, EntryNumber entnum) const
374 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff) }); }
375
377 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
378 std::shared_ptr<T> get(Cmper partId, Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt) const
379 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, cmper1, cmper2, inci}); }
380
382 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
383 std::shared_ptr<T> get(Cmper partId, EntryNumber entnum, std::optional<Inci> inci = std::nullopt) const
384 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff), inci}); }
385
392 template <typename T, typename std::enable_if_t<std::is_base_of_v<NoteDetailsBase, T>, int> = 0>
393 std::shared_ptr<T> getForNote(const NoteInfoPtr& noteInfo, const std::optional<Cmper>& forPartId = std::nullopt)
394 {
395 auto details = getArray<T>(
396 forPartId.value_or(noteInfo.getEntryInfo().getFrame()->getRequestedPartId()),
397 noteInfo.getEntryInfo()->getEntry()->getEntryNumber()
398 );
399 for (const auto& detail : details) {
400 if (detail->getNoteId() == noteInfo->getNoteId()) {
401 return detail;
402 }
403 }
404 return nullptr;
405 }
406};
407
409using DetailsPoolPtr = std::shared_ptr<DetailsPool>;
410
412class EntryPool // uses different implementation than other pools for more efficient access
413{
414public:
416 void add(EntryNumber entryNumber, const std::shared_ptr<Entry>& instance)
417 {
418 auto [it, emplaced] = m_pool.emplace(entryNumber, instance);
419 if (!emplaced) {
420 MUSX_INTEGRITY_ERROR("Entry number " + std::to_string(entryNumber) + " added twice.");
421 }
422 }
423
425 std::shared_ptr<Entry> get(EntryNumber entryNumber) const
426 {
427 const auto it = m_pool.find(entryNumber);
428 if (it == m_pool.end()) {
429 return nullptr;
430 }
431 return it->second;
432 }
433
434private:
435 std::unordered_map<EntryNumber, std::shared_ptr<Entry>> m_pool;
436};
438using EntryPoolPtr = std::shared_ptr<EntryPool>;
439
441class TextsPool : protected ObjectPool<TextsBase>
442{
443public:
445 void add(const std::string& nodeName, const std::shared_ptr<TextsBase>& instance)
446 {
447 if (instance->getPartId()) {
448 MUSX_INTEGRITY_ERROR("Texts node " + nodeName + " hase non-zero part id [" + std::to_string(instance->getPartId()) + "]");
449 }
450 ObjectPool::add({ nodeName, instance->getPartId(), instance->getTextNumber() }, instance);
451 }
452
454 template <typename T>
455 std::vector<std::shared_ptr<T>> getArray(std::optional<Cmper> cmper = std::nullopt) const
456 { return ObjectPool::getArray<T>({ std::string(T::XmlNodeName), SCORE_PARTID, cmper }); }
457
459 template <typename T>
460 std::shared_ptr<T> get(Cmper cmper) const
461 { return ObjectPool::get<T>({ std::string(T::XmlNodeName), SCORE_PARTID, cmper, std::nullopt, std::nullopt }); }
462};
464using TextsPoolPtr = std::shared_ptr<TextsPool>;
465
466} // namespace dom
467} // namespace musx
ShareMode
Describes how this instance is shared between part and score.
Definition BaseClasses.h:68
A pool that manages collections of DetailsBase objects, organized by XML node names and Cmper values.
Definition ObjectPool.h:347
void add(const std::string &nodeName, const std::shared_ptr< DetailsBase > &instance)
DetailsPool version of ObjectPool::add.
Definition ObjectPool.h:358
std::vector< std::shared_ptr< T > > getArray(Cmper partId, Cmper cmper1, std::optional< Cmper > cmper2=std::nullopt) const
DetailsPool version of ObjectPool::getArray.
Definition ObjectPool.h:368
std::vector< std::shared_ptr< T > > getArray(Cmper partId) const
version of ObjectPool::getArray for getting all of them
Definition ObjectPool.h:363
DetailsPool()
Constructor.
Definition ObjectPool.h:350
std::shared_ptr< T > get(Cmper partId, Cmper cmper1, Cmper cmper2, std::optional< Inci > inci=std::nullopt) const
DetailsPool version of ObjectPool::get.
Definition ObjectPool.h:378
std::shared_ptr< T > getForNote(const NoteInfoPtr &noteInfo, const std::optional< Cmper > &forPartId=std::nullopt)
Returns the detail for a particular note.
Definition ObjectPool.h:393
std::vector< std::shared_ptr< T > > getArray(Cmper partId, EntryNumber entnum) const
EntryDetailsPool version of ObjectPool::getArray.
Definition ObjectPool.h:373
std::shared_ptr< T > get(Cmper partId, EntryNumber entnum, std::optional< Inci > inci=std::nullopt) const
EntryDetailsPool version of ObjectPool::get.
Definition ObjectPool.h:383
std::shared_ptr< const EntryFrame > getFrame() const
Returns the frame.
Definition Entries.h:300
Entry pool.
Definition ObjectPool.h:413
void add(EntryNumber entryNumber, const std::shared_ptr< Entry > &instance)
Add an entry to the EntryPool. (Used by the factory.)
Definition ObjectPool.h:416
std::shared_ptr< Entry > get(EntryNumber entryNumber) const
Get an entry from the EntryPool.
Definition ObjectPool.h:425
Wraps an EntryInfo instance and a note index.
Definition Entries.h:670
EntryInfoPtr getEntryInfo() const
Gets the entry info for this note.
Definition Entries.h:705
A pool that manages collections of OthersBase objects, organized by XML node names and Cmper values.
Definition ObjectPool.h:55
std::shared_ptr< 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:242
std::shared_ptr< T > get(const ObjectKey &key) const
Retrieves the first (and usually only) object of a specific type from the pool.
Definition ObjectPool.h:220
virtual ~ObjectPool()=default
virtual destructor
std::vector< std::shared_ptr< T > > getArray(const ObjectKey &key) const
Retrieves a vector of objects of a specific type from the pool.
Definition ObjectPool.h:141
std::vector< std::shared_ptr< T > > getArrayForPart(const ObjectKey &key) const
Retrieves a vector of objects of a specific type from the pool.
Definition ObjectPool.h:177
ObjectPool(const std::unordered_map< std::string, dom::Base::ShareMode > &knownShareModes={})
Constructs the object pool.
Definition ObjectPool.h:260
void add(const ObjectKey &key, ObjectPtr object)
Adds an OthersBase object to the pool.
Definition ObjectPool.h:104
std::shared_ptr< ObjectBaseType > ObjectPtr
shared pointer to ObjectBaseType
Definition ObjectPool.h:58
A pool that manages collections of OptionsBase objects that have no Cmper value.
Definition ObjectPool.h:273
std::shared_ptr< T > get() const
Scalar version of ObjectPool::get.
Definition ObjectPool.h:293
void add(const std::string &nodeName, const std::shared_ptr< OptionsBase > &instance)
Scalar version of ObjectPool::add.
Definition ObjectPool.h:276
std::vector< std::shared_ptr< T > > getArray() const
Scalar version of ObjectPool::getArray.
Definition ObjectPool.h:286
A pool that manages collections of OthersBase objects.
Definition ObjectPool.h:306
OthersPool()
Constructor.
Definition ObjectPool.h:309
void add(const std::string &nodeName, const std::shared_ptr< OthersBase > &instance)
OthersPool version of ObjectPool::add.
Definition ObjectPool.h:324
std::vector< std::shared_ptr< T > > getArray(Cmper partId, std::optional< Cmper > cmper=std::nullopt) const
OthersPool version of ObjectPool::getArray.
Definition ObjectPool.h:329
std::shared_ptr< T > get(Cmper partId, Cmper cmper, std::optional< Inci > inci=std::nullopt) const
OthersPool version of ObjectPool::get.
Definition ObjectPool.h:334
Text pool.
Definition ObjectPool.h:442
std::vector< std::shared_ptr< T > > getArray(std::optional< Cmper > cmper=std::nullopt) const
Texts version of ObjectPool::getArray.
Definition ObjectPool.h:455
std::shared_ptr< T > get(Cmper cmper) const
Texts version of ObjectPool::get.
Definition ObjectPool.h:460
void add(const std::string &nodeName, const std::shared_ptr< TextsBase > &instance)
Texts version of ObjectPool::add.
Definition ObjectPool.h:445
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition SmartShape.h:417
static constexpr std::string_view XmlNodeName
XML node name for this type.
Definition Details.h:1259
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Details.h:1315
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:360
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:646
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1302
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1350
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1284
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1437
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1582
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2389
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2483
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2506
std::shared_ptr< OthersPool > OthersPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:338
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:80
std::shared_ptr< DetailsPool > DetailsPoolPtr
Shared DetailsPool pointer.
Definition ObjectPool.h:409
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:55
std::shared_ptr< TextsPool > TextsPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:464
std::shared_ptr< OptionsPool > OptionsPoolPtr
Shared OptionsPool pointer.
Definition ObjectPool.h:299
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:68
std::shared_ptr< EntryPool > EntryPoolPtr
Shared EntryPool pointer.
Definition ObjectPool.h:438
object model for musx file (enigmaxml)
Definition BaseClasses.h:32
key type for storing in pool
Definition ObjectPool.h:60
std::optional< Cmper > cmper2
optional cmper2 for Details.
Definition ObjectPool.h:64
std::optional< Cmper > cmper1
optional cmper1 for Others, Texts, Details.
Definition ObjectPool.h:63
Cmper partId
the part this item is associated with (or 0 for score).
Definition ObjectPool.h:62
ObjectKey(const std::string n, Cmper p, std::optional< Cmper > c1=std::nullopt, std::optional< Cmper > c2=std::nullopt, std::optional< Inci > i=std::nullopt)
explicit constructor for optional parameters
Definition ObjectPool.h:68
std::optional< Inci > inci
optional inci for multi-inci classes
Definition ObjectPool.h:65
bool operator<(const ObjectKey &other) const
comparison operator for std::map
Definition ObjectPool.h:77
std::string nodeId
the identifier for this node. usually the XML node name.
Definition ObjectPool.h:61