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
95 std::string description() const
96 {
97 std::string result = nodeId + " part " + std::to_string(partId);
98 if (cmper1) {
99 if (cmper2) {
100 result += " cmpers [" + std::to_string(cmper1.value()) + ", " + std::to_string(cmper2.value()) + "]";
101 } else {
102 result += " cmper " + std::to_string(cmper1.value());
103 }
104 }
105 if (inci) {
106 result += " inci " + std::to_string(inci.value());
107 }
108 return result;
109 }
110 };
111
113 virtual ~ObjectPool() = default;
114
121 void add(const ObjectKey& key, ObjectPtr object)
122 {
123 if (key.inci.has_value()) {
124 ObjectKey noInciKey = key;
125 noInciKey.inci = std::nullopt;
126 auto currentIncis = getArray<ObjectBaseType>(noInciKey);
127 if (key.inci.value() != int(currentIncis.size())) {
128 MUSX_INTEGRITY_ERROR("Node " + key.nodeId + " has inci " + std::to_string(key.inci.value()) + " that is out of sequence.");
129 }
130 }
131 auto [poolIt, emplaced] = m_pool.emplace(key, object);
132 if (!emplaced) {
133 MUSX_INTEGRITY_ERROR("Attempted to add same key more than once: " + key.description());
134 }
135 auto it = m_shareMode.find(key.nodeId);
136 if (it == m_shareMode.end()) {
137 m_shareMode.emplace(key.nodeId, object->getShareMode());
138 } else if (object->getShareMode() != it->second && object->getShareMode() != Base::ShareMode::All) {
139 if (it->second == Base::ShareMode::All) {
140 m_shareMode[key.nodeId] = object->getShareMode();
141 } else {
142 MUSX_INTEGRITY_ERROR("Share mode for added " + key.nodeId + " object [" + std::to_string(int(object->getShareMode()))
143 + "] does not match previous [" + std::to_string(int(it->second)) + "]");
144 }
145 }
146 }
147
160 template <typename T>
161 std::vector<std::shared_ptr<T>> getArray(const ObjectKey& key) const
162 {
163 std::vector<std::shared_ptr<T>> result;
164
165 auto rangeStart = m_pool.lower_bound(key);
166 auto rangeEnd = m_pool.upper_bound(
167 ObjectKey{
168 key.nodeId,
169 key.partId,
170 key.cmper1.value_or((std::numeric_limits<Cmper>::max)()),
171 key.cmper2.value_or((std::numeric_limits<Cmper>::max)()),
172 key.inci.value_or((std::numeric_limits<Inci>::max)())
173 }
174 );
175
176 for (auto it = rangeStart; it != rangeEnd; ++it) {
177 auto typedPtr = std::dynamic_pointer_cast<T>(it->second);
178 assert(typedPtr);
179 result.push_back(typedPtr);
180 }
181 return result;
182 }
183
196 template <typename T>
197 std::vector<std::shared_ptr<T>> getArrayForPart(const ObjectKey& key) const
198 {
200 if (key.partId != SCORE_PARTID) {
201 auto it = m_shareMode.find(key.nodeId);
202 // If the nodeId is not found in m_shareMode, it means the document contains no instances,
203 // and every path will return an empty vector. We can safely ignore this case.
204 if (it != m_shareMode.end()) {
205 forShareMode = it->second;
206 }
207 if (forShareMode == Base::ShareMode::Partial) {
208 if constexpr (std::is_base_of_v<OthersBase, T>) {
209 if (!key.cmper1.has_value()) {
210 throw std::invalid_argument("Array searches on partially shared Others must supply a cmper.");
211 }
212 } else if constexpr (std::is_base_of_v<DetailsBase, T>) {
213 if (!key.cmper1.has_value() || !key.cmper2.has_value()) {
214 throw std::invalid_argument("Array searches on partially shared Details must supply both cmpers.");
215 }
216 }
217 }
218 }
219 auto keyResult = getArray<T>(key);
220 if (!keyResult.empty() || key.partId == SCORE_PARTID || forShareMode == Base::ShareMode::None) {
221 return keyResult;
222 }
223 ObjectKey scoreKey(key);
224 scoreKey.partId = SCORE_PARTID;
225 return getArray<T>(scoreKey);
226 }
227
239 template <typename T>
240 std::shared_ptr<T> get(const ObjectKey& key) const
241 {
242 auto it = m_pool.find(key);
243 if (it == m_pool.end()) {
244 return nullptr;
245 }
246 auto typedPtr = std::dynamic_pointer_cast<T>(it->second);
247 assert(typedPtr); // There is a program bug if the pointer cast fails.
248 return typedPtr;
249 }
250
261 template <typename T>
262 std::shared_ptr<T> getEffectiveForPart(const ObjectKey& key) const
263 {
264 if (auto partVersion = get<T>(key)) {
265 return partVersion;
266 }
267 if (key.partId == SCORE_PARTID) {
268 // if this is already the score version, there is nothing to return.
269 return nullptr;
270 }
271 ObjectKey scoreKey(key);
272 scoreKey.partId = SCORE_PARTID;
273 return get<T>(scoreKey);
274 }
275
276protected:
280 ObjectPool(const std::unordered_map<std::string, dom::Base::ShareMode>& knownShareModes = {})
281 : m_shareMode(knownShareModes) {}
282
283private:
284 std::map<ObjectKey, ObjectPtr> m_pool;
285 std::unordered_map<std::string, dom::Base::ShareMode> m_shareMode;
286};
287
292class OptionsPool : protected ObjectPool<OptionsBase>
293{
294public:
296 void add(const std::string& nodeName, const std::shared_ptr<OptionsBase>& instance)
297 {
298 if (instance->getPartId()) {
299 MUSX_INTEGRITY_ERROR("Options node " + nodeName + " hase non-zero part id [" + std::to_string(instance->getPartId()) + "]");
300 }
301 ObjectPool::add({ nodeName, instance->getPartId() }, instance);
302 }
303
305 template <typename T>
306 std::vector<std::shared_ptr<T>> getArray() const
307 {
308 return ObjectPool::getArray<T>({ std::string(T::XmlNodeName), SCORE_PARTID });
309 }
310
312 template <typename T>
313 std::shared_ptr<T> get() const
314 {
315 return ObjectPool::get<T>({ std::string(T::XmlNodeName), SCORE_PARTID });
316 }
317};
319using OptionsPoolPtr = std::shared_ptr<OptionsPool>;
320
325class OthersPool : public ObjectPool<OthersBase>
326{
327public:
342
344 void add(const std::string& nodeName, const std::shared_ptr<OthersBase>& instance)
345 { ObjectPool::add({nodeName, instance->getPartId(), instance->getCmper(), std::nullopt, instance->getInci()}, instance); }
346
348 template <typename T>
349 std::vector<std::shared_ptr<T>> getArray(Cmper partId, std::optional<Cmper> cmper = std::nullopt) const
350 { return ObjectPool::getArrayForPart<T>({ std::string(T::XmlNodeName), partId, cmper }); }
351
353 template <typename T>
354 std::shared_ptr<T> get(Cmper partId, Cmper cmper, std::optional<Inci> inci = std::nullopt) const
355 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, cmper, std::nullopt, inci}); }
356};
358using OthersPoolPtr = std::shared_ptr<OthersPool>;
359
366class DetailsPool : protected ObjectPool<DetailsBase>
367{
368public:
374 // add other known sharemode none items as they are identified.
375 }) {}
376
378 void add(const std::string& nodeName, const std::shared_ptr<DetailsBase>& instance)
379 { ObjectPool::add({nodeName, instance->getPartId(), instance->getCmper1(), instance->getCmper2(), instance->getInci()}, instance); }
380
382 template <typename T>
383 std::vector<std::shared_ptr<T>> getArray(Cmper partId) const
384 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId }); }
385
387 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
388 std::vector<std::shared_ptr<T>> getArray(Cmper partId, Cmper cmper1, std::optional<Cmper> cmper2 = std::nullopt) const
389 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId, cmper1, cmper2 }); }
390
392 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
393 std::vector<std::shared_ptr<T>> getArray(Cmper partId, EntryNumber entnum) const
394 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff) }); }
395
397 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
398 std::shared_ptr<T> get(Cmper partId, Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt) const
399 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, cmper1, cmper2, inci}); }
400
402 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
403 std::shared_ptr<T> get(Cmper partId, EntryNumber entnum, std::optional<Inci> inci = std::nullopt) const
404 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff), inci}); }
405
412 template <typename T, typename std::enable_if_t<std::is_base_of_v<NoteDetailsBase, T>, int> = 0>
413 std::shared_ptr<T> getForNote(const NoteInfoPtr& noteInfo, const std::optional<Cmper>& forPartId = std::nullopt)
414 {
415 auto details = getArray<T>(
416 forPartId.value_or(noteInfo.getEntryInfo().getFrame()->getRequestedPartId()),
417 noteInfo.getEntryInfo()->getEntry()->getEntryNumber()
418 );
419 for (const auto& detail : details) {
420 if (detail->getNoteId() == noteInfo->getNoteId()) {
421 return detail;
422 }
423 }
424 return nullptr;
425 }
426};
427
429using DetailsPoolPtr = std::shared_ptr<DetailsPool>;
430
432class EntryPool // uses different implementation than other pools for more efficient access
433{
434public:
436 void add(EntryNumber entryNumber, const std::shared_ptr<Entry>& instance)
437 {
438 auto [it, emplaced] = m_pool.emplace(entryNumber, instance);
439 if (!emplaced) {
440 MUSX_INTEGRITY_ERROR("Entry number " + std::to_string(entryNumber) + " added twice.");
441 }
442 }
443
445 std::shared_ptr<Entry> get(EntryNumber entryNumber) const
446 {
447 const auto it = m_pool.find(entryNumber);
448 if (it == m_pool.end()) {
449 return nullptr;
450 }
451 return it->second;
452 }
453
454private:
455 std::unordered_map<EntryNumber, std::shared_ptr<Entry>> m_pool;
456};
458using EntryPoolPtr = std::shared_ptr<EntryPool>;
459
461class TextsPool : protected ObjectPool<TextsBase>
462{
463public:
465 void add(const std::string& nodeName, const std::shared_ptr<TextsBase>& instance)
466 {
467 if (instance->getPartId()) {
468 MUSX_INTEGRITY_ERROR("Texts node " + nodeName + " hase non-zero part id [" + std::to_string(instance->getPartId()) + "]");
469 }
470 ObjectPool::add({ nodeName, instance->getPartId(), instance->getTextNumber() }, instance);
471 }
472
474 template <typename T>
475 std::vector<std::shared_ptr<T>> getArray(std::optional<Cmper> cmper = std::nullopt) const
476 { return ObjectPool::getArray<T>({ std::string(T::XmlNodeName), SCORE_PARTID, cmper }); }
477
479 template <typename T>
480 std::shared_ptr<T> get(Cmper cmper) const
481 { return ObjectPool::get<T>({ std::string(T::XmlNodeName), SCORE_PARTID, cmper, std::nullopt, std::nullopt }); }
482};
484using TextsPoolPtr = std::shared_ptr<TextsPool>;
485
486} // namespace dom
487} // 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:367
void add(const std::string &nodeName, const std::shared_ptr< DetailsBase > &instance)
DetailsPool version of ObjectPool::add.
Definition ObjectPool.h:378
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:388
std::vector< std::shared_ptr< T > > getArray(Cmper partId) const
version of ObjectPool::getArray for getting all of them
Definition ObjectPool.h:383
DetailsPool()
Constructor.
Definition ObjectPool.h:370
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:398
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:413
std::vector< std::shared_ptr< T > > getArray(Cmper partId, EntryNumber entnum) const
EntryDetailsPool version of ObjectPool::getArray.
Definition ObjectPool.h:393
std::shared_ptr< T > get(Cmper partId, EntryNumber entnum, std::optional< Inci > inci=std::nullopt) const
EntryDetailsPool version of ObjectPool::get.
Definition ObjectPool.h:403
std::shared_ptr< const EntryFrame > getFrame() const
Returns the frame.
Definition Entries.h:411
Entry pool.
Definition ObjectPool.h:433
void add(EntryNumber entryNumber, const std::shared_ptr< Entry > &instance)
Add an entry to the EntryPool. (Used by the factory.)
Definition ObjectPool.h:436
std::shared_ptr< Entry > get(EntryNumber entryNumber) const
Get an entry from the EntryPool.
Definition ObjectPool.h:445
Wraps an EntryInfo instance and a note index.
Definition Entries.h:833
EntryInfoPtr getEntryInfo() const
Gets the entry info for this note.
Definition Entries.h:868
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:262
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:240
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:161
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:197
ObjectPool(const std::unordered_map< std::string, dom::Base::ShareMode > &knownShareModes={})
Constructs the object pool.
Definition ObjectPool.h:280
void add(const ObjectKey &key, ObjectPtr object)
Adds an OthersBase object to the pool.
Definition ObjectPool.h:121
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:293
std::shared_ptr< T > get() const
Scalar version of ObjectPool::get.
Definition ObjectPool.h:313
void add(const std::string &nodeName, const std::shared_ptr< OptionsBase > &instance)
Scalar version of ObjectPool::add.
Definition ObjectPool.h:296
std::vector< std::shared_ptr< T > > getArray() const
Scalar version of ObjectPool::getArray.
Definition ObjectPool.h:306
A pool that manages collections of OthersBase objects.
Definition ObjectPool.h:326
OthersPool()
Constructor.
Definition ObjectPool.h:329
void add(const std::string &nodeName, const std::shared_ptr< OthersBase > &instance)
OthersPool version of ObjectPool::add.
Definition ObjectPool.h:344
std::vector< std::shared_ptr< T > > getArray(Cmper partId, std::optional< Cmper > cmper=std::nullopt) const
OthersPool version of ObjectPool::getArray.
Definition ObjectPool.h:349
std::shared_ptr< T > get(Cmper partId, Cmper cmper, std::optional< Inci > inci=std::nullopt) const
OthersPool version of ObjectPool::get.
Definition ObjectPool.h:354
Text pool.
Definition ObjectPool.h:462
std::vector< std::shared_ptr< T > > getArray(std::optional< Cmper > cmper=std::nullopt) const
Texts version of ObjectPool::getArray.
Definition ObjectPool.h:475
std::shared_ptr< T > get(Cmper cmper) const
Texts version of ObjectPool::get.
Definition ObjectPool.h:480
void add(const std::string &nodeName, const std::shared_ptr< TextsBase > &instance)
Texts version of ObjectPool::add.
Definition ObjectPool.h:465
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition SmartShape.h:420
static constexpr std::string_view XmlNodeName
XML node name for this type.
Definition Details.h:1274
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Details.h:1345
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:361
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:649
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1342
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1414
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1324
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1501
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:1646
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2489
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2586
static constexpr std::string_view XmlNodeName
The XML node name for this type.
Definition Others.h:2609
std::shared_ptr< OthersPool > OthersPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:358
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:429
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:55
std::shared_ptr< TextsPool > TextsPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:484
std::shared_ptr< OptionsPool > OptionsPoolPtr
Shared OptionsPool pointer.
Definition ObjectPool.h:319
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:68
std::shared_ptr< EntryPool > EntryPoolPtr
Shared EntryPool pointer.
Definition ObjectPool.h:458
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 description() const
provides a description of the key for diagnostic purposes
Definition ObjectPool.h:95
std::string nodeId
the identifier for this node. usually the XML node name.
Definition ObjectPool.h:61