MNX Document Model
Loading...
Searching...
No Matches
IdMapping.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 <unordered_map>
25#include <memory>
26#include <string>
27#include <tuple>
28#include <stdexcept>
29#include <type_traits>
30#include <optional>
31#include <sstream>
32
33#include "BaseTypes.h"
34#include "Global.h"
35#include "Layout.h"
36#include "Part.h"
37#include "Score.h"
38
39namespace mnx::util {
40
42class mapping_error : public std::runtime_error
43{
44 using runtime_error::runtime_error;
45};
46
54class IdMapping {
55public:
61 explicit IdMapping(std::shared_ptr<json> documentRoot, const std::optional<ErrorHandler>& errorHandler = std::nullopt)
62 : m_root(documentRoot), m_errorHandler(errorHandler) {}
63
73 template <typename T, typename IdType>
74 T get(const IdType& id, const std::optional<Base>& errorLocation = std::nullopt) const
75 {
76 const auto& map = getMap<T>();
77 auto it = map.find(id);
78 if (it == map.end()) {
79 mapping_error err("ID " + formatKeyString(id) + " not found in ID mapping");
80 if (m_errorHandler) m_errorHandler.value()(err.what(), errorLocation.value_or(Document(m_root)));
81 throw err;
82 }
83 return T(m_root, it->second);
84 }
85
87 template <typename T, typename IdType>
88 bool exists(const IdType& id) const
89 {
90 const auto& map = getMap<T>();
91 return map.find(id) != map.end();
92 }
93
100 template <typename T, typename IdType>
101 void add(const IdType& id, const T& value)
102 {
103 auto result = getMap<T>().emplace(id, value.pointer());
104 if (!result.second) {
105 mapping_error err("ID " + formatKeyString(id) + " already exists at " + result.first->second.to_string());
106 if (m_errorHandler) {
107 m_errorHandler.value()(err.what(), value);
108 } else {
109 throw err;
110 }
111 }
112 }
113
118 mnx::part::Measure getPartMeasure(const mnx::global::Measure& globalMeasure, const std::string& partId) const
119 {
120 const size_t measureIndex = globalMeasure.calcArrayIndex();
121 const auto part = get<mnx::Part>(partId);
122 const auto measures = part.measures();
123 if (!measures || measureIndex >= measures.value().size()) {
124 mapping_error err("Part \'" + partId + "\" lacks a corresponding measure for the input global measure.");
125 if (m_errorHandler) m_errorHandler.value()(err.what(), globalMeasure);
126 throw err;
127 }
128 return measures.value()[measureIndex];
129 }
130
131private:
132 std::shared_ptr<json> m_root;
133 std::optional<ErrorHandler> m_errorHandler;
134
135 using Map = std::unordered_map<std::string, json_pointer>;
136 Map m_eventMap;
137 Map m_layoutMap;
138 Map m_noteMap;
139 Map m_partMap;
140
141 std::unordered_map<int, json_pointer> m_globalMeasures;
142
143 // stoopid workround
144 template <typename>
145 static constexpr bool always_false = false;
146
147 template <typename T, typename Self>
148 static auto& getMapImpl(Self& self) {
149 if constexpr (std::is_same_v<T, mnx::Part>) {
150 return self.m_partMap;
151 } else if constexpr (std::is_same_v<T, mnx::Layout>) {
152 return self.m_layoutMap;
153 } else if constexpr (std::is_same_v<T, mnx::sequence::Note>) {
154 return self.m_noteMap;
155 } else if constexpr (std::is_same_v<T, mnx::sequence::Event>) {
156 return self.m_eventMap;
157 } else if constexpr (std::is_same_v<T, mnx::global::Measure>) {
158 return self.m_globalMeasures;
159 } else {
160 static_assert(always_false<T>, "Unsupported type for IdMapping::getMap");
161 }
162 }
163
164 template <typename T>
165 const auto& getMap() const
166 { return getMapImpl<T>(*this); }
167
168 template <typename T>
169 auto& getMap()
170 { return getMapImpl<T>(*this); }
171
172 template <typename KeyType>
173 static std::string formatKeyString(const KeyType& key) {
174 if constexpr (std::is_same_v<KeyType, std::string>) {
175 return "\"" + key + "\"";
176 } else {
177 std::ostringstream oss;
178 oss << key;
179 return oss.str();
180 }
181 }
182};
183
184} // namespace mnx::util
size_t calcArrayIndex() const
Calculates the array index of the current instance within the array.
Definition BaseTypes.h:620
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:87
Represents a single global measure instance within an MNX document.
Definition Global.h:235
Represents a single measuer in a part in an MNX document. It contains the majority of the musical inf...
Definition Part.h:203
Provides type-safe ID-based lookup for elements in an MNX document.
Definition IdMapping.h:54
void add(const IdType &id, const T &value)
Adds a key to the mapping. If there is no error handler, it throws mapping_error if there is a duplic...
Definition IdMapping.h:101
mnx::part::Measure getPartMeasure(const mnx::global::Measure &globalMeasure, const std::string &partId) const
Returns the part::Measure instance associated with the input global::Measure.
Definition IdMapping.h:118
IdMapping(std::shared_ptr< json > documentRoot, const std::optional< ErrorHandler > &errorHandler=std::nullopt)
Constructs the index for a given document.
Definition IdMapping.h:61
T get(const IdType &id, const std::optional< Base > &errorLocation=std::nullopt) const
Looks up an object by string ID.
Definition IdMapping.h:74
bool exists(const IdType &id) const
Returns whether the specified ID exists in the mapping.
Definition IdMapping.h:88
base class for mapping error exceptions
Definition IdMapping.h:43