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) {
81 m_errorHandler.value()(err.what(), errorLocation.value_or(Document(m_root)));
82 }
83 throw err;
84 }
85 if (it->second.typeName != T::JsonSchemaTypeName) {
86 mapping_error err("ID " + formatKeyString(id) + " has type \"" + std::string(it->second.typeName)
87 + "\", but the requested type is \"" + std::string(T::JsonSchemaTypeName) + "\".");
88 if (m_errorHandler) {
89 m_errorHandler.value()(err.what(), errorLocation.value_or(Document(m_root)));
90 }
91 throw err;
92 }
93 return T(m_root, it->second.location);
94 }
95
97 template <typename T, typename IdType>
98 bool exists(const IdType& id) const
99 {
100 const auto& map = getMap<T>();
101 return map.find(id) != map.end();
102 }
103
110 template <typename T, typename IdType>
111 void add(const IdType& id, const T& value)
112 {
113 auto result = getMap<T>().emplace(id, MappedLocation{ value.pointer(), T::JsonSchemaTypeName });
114 if (!result.second) {
115 mapping_error err("ID " + formatKeyString(id) + " already exists for type \"" + std::string(result.first->second.typeName)
116 + "\" at " + result.first->second.location.to_string());
117 if (m_errorHandler) {
118 m_errorHandler.value()(err.what(), value);
119 } else {
120 throw err;
121 }
122 }
123 }
124
125private:
126 std::shared_ptr<json> m_root;
127 std::optional<ErrorHandler> m_errorHandler;
128
129 using MappedLocation = struct
130 {
131 json_pointer location;
132 std::string_view typeName;
133 };
134 std::unordered_map<std::string, MappedLocation> m_objectMap;
135 std::unordered_map<int, MappedLocation> m_globalMeasures;
136
137 template <typename T, typename Self>
138 static auto& getMapImpl(Self& self) {
139 if constexpr (std::is_same_v<T, mnx::global::Measure>) {
140 return self.m_globalMeasures;
141 } else {
142 return self.m_objectMap;
143 }
144 }
145
146 template <typename T>
147 const auto& getMap() const
148 { return getMapImpl<T>(*this); }
149
150 template <typename T>
151 auto& getMap()
152 { return getMapImpl<T>(*this); }
153
154 template <typename KeyType>
155 static std::string formatKeyString(const KeyType& key) {
156 if constexpr (std::is_same_v<KeyType, std::string>) {
157 return "\"" + key + "\"";
158 } else {
159 std::ostringstream oss;
160 oss << key;
161 return oss.str();
162 }
163 }
164};
165
166} // namespace mnx::util
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:87
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:111
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:98
base class for mapping error exceptions
Definition IdMapping.h:43
json::json_pointer json_pointer
JSON pointer class for MNX.
Definition BaseTypes.h:198