32#include <unordered_map>
34#include "../BaseTypes.h"
39#include "../Sequence.h"
44#ifndef DOXYGEN_SHOULD_IGNORE_THIS
47struct JsonSchemaTypeNames
49 static constexpr std::array<std::string_view, 1> value{ T::JsonSchemaTypeName };
53struct JsonSchemaTypeNames<sequence::NoteBase>
55 static constexpr std::array<std::string_view, 2> value{
56 sequence::Note::JsonSchemaTypeName,
57 sequence::KitNote::JsonSchemaTypeName
62bool matchesTypeName(std::string_view typeName)
64 for (
const auto name : JsonSchemaTypeNames<T>::value) {
65 if (name == typeName) {
73std::string typeNamesExpectationString()
75 std::ostringstream oss;
76 const auto& names = JsonSchemaTypeNames<T>::value;
77 const auto count = names.size();
79 oss <<
"expected one of ";
83 for (
size_t i = 0; i < count; ++i) {
87 oss <<
"\"" << names[i] <<
"\"";
98 using runtime_error::runtime_error;
120 std::optional<unsigned>
graceIndex = std::nullopt)
138 template <
typename T>
139 void add(
const std::string&
id,
const T& value)
141 auto result = m_objectMap.emplace(
id, MappedLocation{ value.pointer(), T::JsonSchemaTypeName });
142 if (!result.second) {
143 mapping_error err(
"ID " + formatKeyString(
id) +
" already exists for type \"" + std::string(result.first->second.typeName)
144 +
"\" at " + result.first->second.location.to_string());
145 if (m_errorHandler) {
146 m_errorHandler.value()(err.what(), value);
156 void addEventToBeam(
const std::string& eventId,
const part::Beam& beam)
159 if (!result.second) {
160 mapping_error err(
"ID " + formatKeyString(eventId) +
" already exists in beam "
161 + result.first->second.location.location.to_string());
162 if (m_errorHandler) {
163 m_errorHandler.value()(err.what(), beam);
171 void setEventBeamStartLevel(
const std::string& eventId,
int level)
173 auto it = m_eventsInBeams.find(eventId);
174 if (it == m_eventsInBeams.end()) {
175 throw std::logic_error(
"Attempted to assign a beam start level to unmapped event " + eventId);
177 if (it->second.startLevel == 0) {
178 it->second.startLevel = level;
180 it->second.startLevel = std::min(it->second.startLevel, level);
185 void setEventOttavaShift(
const std::string& eventPointer,
int shift)
187 m_eventOttavaShift[eventPointer] = shift;
191 void setEventPosition(
const std::string& eventPointer,
const MappedPosition& position)
193 m_eventPositions.insert_or_assign(eventPointer, position);
205 explicit EntityMap(std::weak_ptr<json> documentRoot,
const std::optional<ErrorHandler>& errorHandler = std::nullopt)
206 : m_root(documentRoot), m_errorHandler(errorHandler) {}
221 template <
typename T>
223 const std::string&
id,
224 const std::optional<Base>& errorLocation = std::nullopt)
const
226 auto it = m_objectMap.find(
id);
227 if (it == m_objectMap.end()) {
230 MNX_ASSERT_IF(!detail::matchesTypeName<T>(it->second.typeName)) {
232 "ID " + formatKeyString(
id) +
" has type \"" + std::string(it->second.typeName) +
233 "\", but " + detail::typeNamesExpectationString<T>() +
"."
235 if (m_errorHandler) {
236 m_errorHandler.value()(err.what(), errorLocation.value_or(
Document(root())));
240 return T(root(), it->second.location);
251 template <
typename T>
252 T
get(
const std::string&
id,
const std::optional<Base>& errorLocation = std::nullopt)
const
254 if (
auto v = tryGet<T>(
id, errorLocation)) {
255 return *std::move(v);
257 mapping_error err(
"ID " + formatKeyString(
id) +
" not found in ID mapping");
258 if (m_errorHandler) {
259 m_errorHandler.value()(err.what(), errorLocation.value_or(
Document(root())));
275 template <
typename T>
276 size_t getIndexOf(
const std::string&
id,
const std::optional<Base>& errorLocation = std::nullopt)
const
278 static_assert(std::is_base_of_v<ArrayElementObject, T>,
279 "getIndexOf<T> requires T to derive from ArrayElementObject");
281 auto v = get<T>(
id, errorLocation);
282 return v.calcArrayIndex();
287 template <
typename T>
290 const auto it = m_objectMap.find(
id);
291 if (it == m_objectMap.end()) {
294 return detail::matchesTypeName<T>(it->second.typeName);
302 if (
const auto& eventId = event.id()) {
303 const auto it = m_eventsInBeams.find(eventId.value());
304 if (it != m_eventsInBeams.end()) {
307 "The beam mapping for eventId " + formatKeyString(eventId.value())
308 +
" was mapped to an object of type \"" + std::string(it->second.location.typeName) +
"\"."
310 if (m_errorHandler) {
311 m_errorHandler.value()(err.what(), event);
315 return part::Beam(root(), it->second.location.location);
324 const auto it = m_eventsInBeams.find(eventId);
325 if (it == m_eventsInBeams.end()) {
328 return it->second.startLevel;
344 m_eventsInBeams.clear();
345 m_eventPositions.clear();
346 m_eventOttavaShift.clear();
347 m_lyricLineOrder.clear();
353 const auto it = m_eventOttavaShift.find(event.
pointer().to_string());
354 if (it == m_eventOttavaShift.end()) {
364 return shift.value();
372 const auto it = m_eventPositions.find(event.
pointer().to_string());
373 if (it == m_eventPositions.end()) {
383 return *std::move(position);
385 throw std::logic_error(
"No mapped position cached for event at " + event.
pointer().to_string());
401 const MappedPosition& rhs,
402 bool rhsIncludesTrailingGrace =
false)
const;
406 {
return m_lyricLineOrder; }
409 std::weak_ptr<json> m_root;
410 std::optional<ErrorHandler> m_errorHandler;
412 std::shared_ptr<json> root()
const
414 std::shared_ptr<json> result = m_root.lock();
416 throw std::runtime_error(
"ID mapping is invalid because the document was destroyed.");
421 using MappedLocation =
struct
424 std::string_view typeName;
426 std::unordered_map<std::string, MappedLocation> m_objectMap;
427 struct BeamMappingEntry
429 MappedLocation location;
432 std::unordered_map<std::string, BeamMappingEntry> m_eventsInBeams;
433 std::unordered_map<std::string, MappedPosition> m_eventPositions;
434 std::unordered_map<std::string, int> m_eventOttavaShift;
435 std::vector<std::string> m_lyricLineOrder;
437 static std::string formatKeyString(
const std::string& key) {
438 return "\"" + key +
"\"";
json_pointer pointer() const
Returns the json_pointer for this node.
Definition BaseTypes.h:260
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:103
Represents a system on a page in a score.
Definition CommonClasses.h:550
Contains information about each level of beam.
Definition Part.h:97
static constexpr std::string_view JsonSchemaTypeName
required for mapping
Definition Part.h:118
Represents a musical event within a sequence.
Definition Sequence.h:418
Provides type-safe ID-based lookup for elements in an MNX document.
Definition EntityMap.h:109
bool exists(const std::string &id) const
Returns whether the specified ID exists in the mapping with type T.
Definition EntityMap.h:288
std::optional< part::Beam > tryGetBeam(const sequence::Event &event) const
Get the beam for an event, if it is mapped.
Definition EntityMap.h:300
std::optional< int > tryGetOttavaShift(const sequence::Event &event) const
Retrieve the ottava shift for an event (if known).
Definition EntityMap.h:351
std::optional< int > tryGetBeamStartLevel(const std::string &eventId) const
Return the secondary beam depth that starts at an event ID, if any.
Definition EntityMap.h:322
int getBeamStartLevel(const std::string &eventId) const
Return the beam start level for an event ID or 0 if none.
Definition EntityMap.h:332
MappedPosition getEventPosition(const sequence::Event &event) const
Retrieve the cached mapped position for an event.
Definition EntityMap.h:380
const std::vector< std::string > & getLyricLineOrder() const
Retrieve the lyric line order.
Definition EntityMap.h:405
EntityMap(std::weak_ptr< json > documentRoot, const std::optional< ErrorHandler > &errorHandler=std::nullopt)
Constructs the index for a given document.
Definition EntityMap.h:205
size_t getIndexOf(const std::string &id, const std::optional< Base > &errorLocation=std::nullopt) const
Returns the array index of an object identified by ID.
Definition EntityMap.h:276
void clear()
Clears all mapped items.
Definition EntityMap.h:341
int comparePositions(const MappedPosition &lhs, const MappedPosition &rhs, bool rhsIncludesTrailingGrace=false) const
Compare two mapped positions using global measure order and grace-note targeting.
Definition Implementations.cpp:828
std::optional< T > tryGet(const std::string &id, const std::optional< Base > &errorLocation=std::nullopt) const
Attempts to look up an object by string ID.
Definition EntityMap.h:222
int getOttavaShift(const sequence::Event &event) const
Retrieve the ottava shift for an event. Returns 0 if not cached.
Definition EntityMap.h:361
T get(const std::string &id, const std::optional< Base > &errorLocation=std::nullopt) const
Looks up an object by string ID.
Definition EntityMap.h:252
std::optional< MappedPosition > tryGetEventPosition(const sequence::Event &event) const
Retrieve the cached mapped position for an event (if known).
Definition EntityMap.h:370
base class for mapping error exceptions
Definition EntityMap.h:97
json::json_pointer json_pointer
JSON pointer class for MNX.
Definition BaseTypes.h:68
Represents a detached arithmetic fraction with normalization.
Definition CommonClasses.h:74
Canonical position value used by EntityMap for measure-relative comparisons and caches.
Definition EntityMap.h:113
MappedPosition(const std::string &measureId, const FractionValue &fraction, std::optional< unsigned > graceIndex=std::nullopt)
Constructor function.
Definition EntityMap.h:119
std::optional< unsigned > graceIndex
the grace index of the event, if any
Definition EntityMap.h:116
MappedPosition(const MeasureRhythmicPosition &position)
Constructor function from MeasureRhythmicPosition.
Definition EntityMap.h:126
FractionValue fraction
the rhythmic position within the measure
Definition EntityMap.h:115
std::string measureId
the measure id of the measure
Definition EntityMap.h:114