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 "Entries.h"
37
38namespace musx {
39namespace dom {
40
50template <typename ObjectBaseType, typename TopKeyElementType = std::string>
52{
53public:
55 using ObjectPtr = std::shared_ptr<ObjectBaseType>;
57 struct ObjectKey {
58 TopKeyElementType nodeId;
60 std::optional<Cmper> cmper1;
61 std::optional<Cmper> cmper2;
62 std::optional<Inci> inci;
63
65 ObjectKey(const TopKeyElementType n,
66 Cmper p,
67 std::optional<Cmper> c1 = std::nullopt,
68 std::optional<Cmper> c2 = std::nullopt,
69 std::optional<Inci> i = std::nullopt) : nodeId(n), partId(p), cmper1(c1), cmper2(c2), inci(i)
70 {
71 }
72
74 bool operator<(const ObjectKey& other) const
75 {
76 if (nodeId != other.nodeId) {
77 return nodeId < other.nodeId;
78 }
79 if (partId != other.partId) {
80 return partId < other.partId;
81 }
82 if (cmper1 != other.cmper1) {
83 return cmper1 < other.cmper1;
84 }
85 if (cmper2 != other.cmper2) {
86 return cmper2 < other.cmper2;
87 }
88 return inci < other.inci;
89 }
90
92 std::string nodeString() const
93 {
94 if constexpr (std::is_same_v<TopKeyElementType, std::string>) {
95 return nodeId;
96 } else {
97 return std::string("entry ") + std::to_string(nodeId);
98 }
99 }
100 };
101
103 virtual ~ObjectPool() = default;
104
111 void add(const ObjectKey& key, ObjectPtr object)
112 {
113 if (key.inci.has_value()) {
114 ObjectKey noInciKey = key;
115 noInciKey.inci = std::nullopt;
116 auto currentIncis = getArray<ObjectBaseType>(noInciKey);
117 if (key.inci.value() != currentIncis.size()) {
118 MUSX_INTEGRITY_ERROR("Node " + key.nodeString() + " has inci " + std::to_string(key.inci.value()) + " that is out of sequence.");
119 }
120 }
121 m_pool.emplace(key, object);
122 auto it = m_shareMode.find(key.nodeId);
123 if (it == m_shareMode.end()) {
124 m_shareMode.emplace(key.nodeId, object->getShareMode());
125 } else if (object->getShareMode() != it->second && object->getShareMode() != Base::ShareMode::All) {
126 if (it->second == Base::ShareMode::All) {
127 m_shareMode[key.nodeId] = object->getShareMode();
128 } else {
129 MUSX_INTEGRITY_ERROR("Share mode for added " + key.nodeString() + " object [" + std::to_string(int(object->getShareMode()))
130 + "] does not match previous [" + std::to_string(int(it->second)) + "]");
131 }
132 }
133 }
134
147 template <typename T>
148 std::vector<std::shared_ptr<T>> getArray(const ObjectKey& key) const
149 {
150 std::vector<std::shared_ptr<T>> result;
151
152 auto rangeStart = m_pool.lower_bound(key);
153 auto rangeEnd = m_pool.upper_bound(
154 ObjectKey{
155 key.nodeId,
156 key.partId,
157 key.cmper1.value_or((std::numeric_limits<Cmper>::max)()),
158 key.cmper2.value_or((std::numeric_limits<Cmper>::max)()),
159 key.inci.value_or((std::numeric_limits<Inci>::max)())
160 }
161 );
162
163 for (auto it = rangeStart; it != rangeEnd; ++it) {
164 auto typedPtr = std::dynamic_pointer_cast<T>(it->second);
165 assert(typedPtr);
166 result.push_back(typedPtr);
167 }
168 return result;
169 }
170
183 template <typename T>
184 std::vector<std::shared_ptr<T>> getArrayForPart(const ObjectKey& key) const
185 {
187 if (key.partId != SCORE_PARTID) {
188 auto it = m_shareMode.find(key.nodeId);
189 // If the nodeId is not found in m_shareMode, it means the document contains no instances,
190 // and every path will return an empty vector. We can safely ignore this case.
191 if (it != m_shareMode.end()) {
192 forShareMode = it->second;
193 }
194 if (forShareMode == Base::ShareMode::Partial) {
195 if constexpr (std::is_base_of_v<OthersBase, T>) {
196 if (!key.cmper1.has_value()) {
197 throw std::invalid_argument("Array searches on partially shared Others must supply a cmper.");
198 }
199 } else if constexpr (std::is_base_of_v<DetailsBase, T>) {
200 if (!key.cmper1.has_value() || !key.cmper2.has_value()) {
201 throw std::invalid_argument("Array searches on partially shared Details must supply both cmpers.");
202 }
203 }
204 }
205 }
206 auto keyResult = getArray<T>(key);
207 if (!keyResult.empty() || key.partId == SCORE_PARTID || forShareMode == Base::ShareMode::None) {
208 return keyResult;
209 }
210 ObjectKey scoreKey(key);
211 scoreKey.partId = SCORE_PARTID;
212 return getArray<T>(scoreKey);
213 }
214
226 template <typename T>
227 std::shared_ptr<T> get(const ObjectKey& key) const
228 {
229 auto it = m_pool.find(key);
230 if (it == m_pool.end()) {
231 return nullptr;
232 }
233 auto typedPtr = std::dynamic_pointer_cast<T>(it->second);
234 assert(typedPtr); // There is a program bug if the pointer cast fails.
235 return typedPtr;
236 }
237
248 template <typename T>
249 std::shared_ptr<T> getEffectiveForPart(const ObjectKey& key) const
250 {
251 if (auto partVersion = get<T>(key)) {
252 return partVersion;
253 }
254 if (key.partId == SCORE_PARTID) {
255 // if this is already the score version, there is nothing to return.
256 return nullptr;
257 }
258 ObjectKey scoreKey(key);
259 scoreKey.partId = SCORE_PARTID;
260 return get<T>(scoreKey);
261 }
262
263protected:
264 // prevent standalone construction
265 ObjectPool() = default;
266
267private:
268 std::map<ObjectKey, ObjectPtr> m_pool;
269 std::unordered_map<TopKeyElementType, dom::Base::ShareMode> m_shareMode;
270};
271
276class OptionsPool : protected ObjectPool<OptionsBase>
277{
278public:
280 void add(const std::string& nodeName, const std::shared_ptr<OptionsBase>& instance)
281 {
282 if (instance->getPartId()) {
283 MUSX_INTEGRITY_ERROR("Options node " + nodeName + " hase non-zero part id [" + std::to_string(instance->getPartId()) + "]");
284 }
285 ObjectPool::add({ nodeName, instance->getPartId() }, instance);
286 }
287
289 template <typename T>
290 std::vector<std::shared_ptr<T>> getArray() const
291 {
292 return ObjectPool::getArray<T>({ std::string(T::XmlNodeName), SCORE_PARTID });
293 }
294
296 template <typename T>
297 std::shared_ptr<T> get() const
298 {
299 return ObjectPool::get<T>({ std::string(T::XmlNodeName), SCORE_PARTID });
300 }
301};
303using OptionsPoolPtr = std::shared_ptr<OptionsPool>;
304
309class OthersPool : public ObjectPool<OthersBase>
310{
311public:
313 void add(const std::string& nodeName, const std::shared_ptr<OthersBase>& instance)
314 { ObjectPool::add({nodeName, instance->getPartId(), instance->getCmper(), std::nullopt, instance->getInci()}, instance); }
315
317 template <typename T>
318 std::vector<std::shared_ptr<T>> getArray(Cmper partId, std::optional<Cmper> cmper = std::nullopt) const
319 { return ObjectPool::getArrayForPart<T>({ std::string(T::XmlNodeName), partId, cmper }); }
320
322 template <typename T>
323 std::shared_ptr<T> get(Cmper partId, Cmper cmper, std::optional<Inci> inci = std::nullopt) const
324 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, cmper, std::nullopt, inci}); }
325};
327using OthersPoolPtr = std::shared_ptr<OthersPool>;
328
335class DetailsPool : protected ObjectPool<DetailsBase>
336{
337public:
339 void add(const std::string& nodeName, const std::shared_ptr<DetailsBase>& instance)
340 { ObjectPool::add({nodeName, instance->getPartId(), instance->getCmper1(), instance->getCmper2(), instance->getInci()}, instance); }
341
343 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
344 std::vector<std::shared_ptr<T>> getArray(Cmper partId, Cmper cmper1, std::optional<Cmper> cmper2 = std::nullopt) const
345 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId, cmper1, cmper2 }); }
346
348 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
349 std::vector<std::shared_ptr<T>> getArray(Cmper partId, EntryNumber entnum) const
350 { return ObjectPool::template getArrayForPart<T>({ std::string(T::XmlNodeName), partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff) }); }
351
353 template <typename T, typename std::enable_if_t<!std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
354 std::shared_ptr<T> get(Cmper partId, Cmper cmper1, Cmper cmper2, std::optional<Inci> inci = std::nullopt) const
355 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, cmper1, cmper2, inci}); }
356
358 template <typename T, typename std::enable_if_t<std::is_base_of_v<EntryDetailsBase, T>, int> = 0>
359 std::shared_ptr<T> get(Cmper partId, EntryNumber entnum, std::optional<Inci> inci = std::nullopt) const
360 { return ObjectPool::getEffectiveForPart<T>({std::string(T::XmlNodeName), partId, Cmper(entnum >> 16), Cmper(entnum & 0xffff), inci}); }
361
363 template <typename T, typename std::enable_if_t<std::is_base_of_v<NoteDetailsBase, T>, int> = 0>
364 std::shared_ptr<T> getForNote(const NoteInfoPtr noteInfo)
365 {
366 auto entry = noteInfo.getEntryInfo()->getEntry();
367 auto details = getArray<T>(entry->getPartId(), entry->getEntryNumber());
368 for (const auto& detail : details) {
369 if (detail->getNoteId() == noteInfo->getNoteId()) {
370 return detail;
371 }
372 }
373 return nullptr;
374 }
375};
376
378using DetailsPoolPtr = std::shared_ptr<DetailsPool>;
379
381class EntryPool : protected ObjectPool<Entry, EntryNumber>
382{
383public:
385 void add(EntryNumber entryNumber, const std::shared_ptr<Entry>& instance)
386 { ObjectPool::add({ entryNumber, SCORE_PARTID }, instance); }
387
389 template <typename T>
390 std::shared_ptr<T> get(EntryNumber entryNumber) const
391 { return ObjectPool::get<T>({ entryNumber, SCORE_PARTID }); }
392};
394using EntryPoolPtr = std::shared_ptr<EntryPool>;
395
397class TextsPool : protected ObjectPool<TextsBase>
398{
399public:
401 void add(const std::string& nodeName, const std::shared_ptr<TextsBase>& instance)
402 {
403 if (instance->getPartId()) {
404 MUSX_INTEGRITY_ERROR("Texts node " + nodeName + " hase non-zero part id [" + std::to_string(instance->getPartId()) + "]");
405 }
406 ObjectPool::add({ nodeName, instance->getPartId(), instance->getTextNumber() }, instance);
407 }
408
410 template <typename T>
411 std::vector<std::shared_ptr<T>> getArray(std::optional<Cmper> cmper = std::nullopt) const
412 { return ObjectPool::getArray<T>({ std::string(T::XmlNodeName), SCORE_PARTID, cmper }); }
413
415 template <typename T>
416 std::shared_ptr<T> get(Cmper cmper) const
417 { return ObjectPool::get<T>({ std::string(T::XmlNodeName), SCORE_PARTID, cmper, std::nullopt, std::nullopt }); }
418};
420using TextsPoolPtr = std::shared_ptr<TextsPool>;
421
422} // namespace dom
423} // namespace musx
ShareMode
Describes how this instance is shared between part and score.
Definition BaseClasses.h:72
A pool that manages collections of DetailsBase objects, organized by XML node names and Cmper values.
Definition ObjectPool.h:336
void add(const std::string &nodeName, const std::shared_ptr< DetailsBase > &instance)
DetailsPool version of ObjectPool::add.
Definition ObjectPool.h:339
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:344
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:354
std::shared_ptr< T > getForNote(const NoteInfoPtr noteInfo)
Returns the detail for a particular note.
Definition ObjectPool.h:364
std::vector< std::shared_ptr< T > > getArray(Cmper partId, EntryNumber entnum) const
EntryDetailsPool version of ObjectPool::getArray.
Definition ObjectPool.h:349
std::shared_ptr< T > get(Cmper partId, EntryNumber entnum, std::optional< Inci > inci=std::nullopt) const
EntryDetailsPool version of ObjectPool::get.
Definition ObjectPool.h:359
Entry pool.
Definition ObjectPool.h:382
void add(EntryNumber entryNumber, const std::shared_ptr< Entry > &instance)
EntryPool version of ObjectPool::add.
Definition ObjectPool.h:385
std::shared_ptr< T > get(EntryNumber entryNumber) const
EntryPool version of ObjectPool::get.
Definition ObjectPool.h:390
Wraps an EntryInfo instance and a note index.
Definition Entries.h:535
EntryInfoPtr getEntryInfo() const
Gets the entry info for this note.
Definition Entries.h:565
A pool that manages collections of OthersBase objects, organized by XML node names and Cmper values.
Definition ObjectPool.h:52
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:249
std::shared_ptr< ObjectBaseType > ObjectPtr
shared pointer to ObjectBaseType
Definition ObjectPool.h:55
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:148
virtual ~ObjectPool()=default
virtual destructor
void add(const ObjectKey &key, ObjectPtr object)
Adds an OthersBase object to the pool.
Definition ObjectPool.h:111
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:184
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:227
A pool that manages collections of OptionsBase objects that have no Cmper value.
Definition ObjectPool.h:277
std::shared_ptr< T > get() const
Scalar version of ObjectPool::get.
Definition ObjectPool.h:297
void add(const std::string &nodeName, const std::shared_ptr< OptionsBase > &instance)
Scalar version of ObjectPool::add.
Definition ObjectPool.h:280
std::vector< std::shared_ptr< T > > getArray() const
Scalar version of ObjectPool::getArray.
Definition ObjectPool.h:290
A pool that manages collections of OthersBase objects.
Definition ObjectPool.h:310
void add(const std::string &nodeName, const std::shared_ptr< OthersBase > &instance)
OthersPool version of ObjectPool::add.
Definition ObjectPool.h:313
std::vector< std::shared_ptr< T > > getArray(Cmper partId, std::optional< Cmper > cmper=std::nullopt) const
OthersPool version of ObjectPool::getArray.
Definition ObjectPool.h:318
std::shared_ptr< T > get(Cmper partId, Cmper cmper, std::optional< Inci > inci=std::nullopt) const
OthersPool version of ObjectPool::get.
Definition ObjectPool.h:323
Text pool.
Definition ObjectPool.h:398
std::vector< std::shared_ptr< T > > getArray(std::optional< Cmper > cmper=std::nullopt) const
Texts version of ObjectPool::getArray.
Definition ObjectPool.h:411
std::shared_ptr< T > get(Cmper cmper) const
Texts version of ObjectPool::get.
Definition ObjectPool.h:416
void add(const std::string &nodeName, const std::shared_ptr< TextsBase > &instance)
Texts version of ObjectPool::add.
Definition ObjectPool.h:401
std::shared_ptr< OthersPool > OthersPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:327
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:75
std::shared_ptr< DetailsPool > DetailsPoolPtr
Shared DetailsPool pointer.
Definition ObjectPool.h:378
uint16_t Cmper
Enigma "comperator" key type.
Definition Fundamentals.h:55
std::shared_ptr< TextsPool > TextsPoolPtr
Shared OthersPool pointer.
Definition ObjectPool.h:420
std::shared_ptr< OptionsPool > OptionsPoolPtr
Shared OptionsPool pointer.
Definition ObjectPool.h:303
int32_t EntryNumber
Entry identifier.
Definition Fundamentals.h:68
std::shared_ptr< EntryPool > EntryPoolPtr
Shared EntryPool pointer.
Definition ObjectPool.h:394
object model for musx file (enigmaxml)
Definition BaseClasses.h:32
key type for storing in pool
Definition ObjectPool.h:57
ObjectKey(const TopKeyElementType 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:65
std::string nodeString() const
Returns a string version of the nodeId for inclusion in messages.
Definition ObjectPool.h:92
std::optional< Cmper > cmper1
optional cmper1 for Others, Texts, Details.
Definition ObjectPool.h:60
Cmper partId
the part this item is associated with (or 0 for score).
Definition ObjectPool.h:59
std::optional< Inci > inci
optional inci for multi-inci classes
Definition ObjectPool.h:62
bool operator<(const ObjectKey &other) const
comparison operator for std::map
Definition ObjectPool.h:74
TopKeyElementType nodeId
the identifier for this node. usually the XML node name.
Definition ObjectPool.h:58
std::optional< Cmper > cmper2
optional cmper2 for Details.
Definition ObjectPool.h:61