MNX Document Model
Loading...
Searching...
No Matches
Document.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 <filesystem>
25#include <fstream>
26
27#include "BaseTypes.h"
28#include "MnxdomProvenance.h"
29#include "Global.h"
30#include "Layout.h"
31#include "Part.h"
32#include "Score.h"
33
34namespace mnx {
35
36namespace util {
37class EntityMap;
38}
39
54
59class MnxMetaData : public Object
60{
61public:
62 using Object::Object;
63
67 MnxMetaData(Base& parent, std::string_view key)
68 : Object(parent, key)
69 {
70 // required children
71 set_version(getMnxSchemaVersion());
72 auto extensions = ensure__x();
74 }
75
80 class Support : public Object
81 {
82 public:
83 using Object::Object;
84
85 MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(bool, useAccidentalDisplay, false);
86 MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(bool, useBeams, false);
87 };
88
92 MNX_REQUIRED_PROPERTY(int, version);
93
98
100 [[nodiscard]] std::optional<MnxdomExtensionData> mnxdom() const
101 {
102 if (const auto value = getExtension(std::string(MNXDOM_PROVENANCE_KEY)); value.has_value() && value->is_object()) {
103 return MnxdomExtensionData(root(), pointer() / "_x" / std::string(MNXDOM_PROVENANCE_KEY));
104 }
105 return std::nullopt;
106 }
107
110 {
111 if (auto existing = mnxdom()) {
112 return *existing;
113 }
114
115 auto extensions = ensure__x();
116 return MnxdomExtensionData(extensions, MNXDOM_PROVENANCE_KEY);
117 }
118
121 {
123 if (auto extensions = _x(); extensions && extensions->empty()) {
124 clear__x();
125 }
126 }
127};
128
133class Document : public Object
134{
135private:
137 std::shared_ptr<util::EntityMap> m_entityMapping;
138
139public:
145 {
146 // create required children
147 create_mnx();
148 create_global();
149 create_parts();
150 }
151
154 Document(const std::shared_ptr<json>& root) : Object(root, json_pointer{}) {}
155
157 Document(const Document& src) : Object(src), m_entityMapping(nullptr) {}
158
159 using Base::root;
160
165 Document(std::istream& inputStream) : Object(std::make_shared<json>(json::object()), json_pointer{})
166 {
167 inputStream >> *root();
168 }
169
175
182 [[nodiscard]] static Document create(const std::filesystem::path& inputPath)
183 {
184 std::ifstream jsonFile;
185 jsonFile.exceptions(std::ios::failbit | std::ios::badbit);
186 jsonFile.open(inputPath);
187 if (!jsonFile.is_open()) {
188 throw std::runtime_error("Unable to open JSON file: " + inputPath.string());
189 }
190 return Document(jsonFile);
191 }
192
202 template <typename Byte,
203 typename = std::enable_if_t<
204 std::is_integral_v<Byte> &&
205 (sizeof(Byte) == 1) &&
206 !std::is_same_v<std::remove_cv_t<Byte>, bool>>>
207 [[nodiscard]] static Document create(const Byte* data, std::size_t size)
208 {
209 if (!data && size != 0) {
210 throw std::invalid_argument("JSON buffer pointer is null.");
211 }
212 auto root = std::make_shared<json>(nlohmann::json::parse(data, data + size));
213 return Document(root);
214 }
215
222 void save(const std::filesystem::path& outputPath, std::optional<int> indentSpaces) const
223 {
224 std::ofstream file;
225 file.exceptions(std::ofstream::failbit | std::ofstream::badbit);
226 file.open(outputPath, std::ios::out | std::ios::binary);
227 if (!file.is_open()) {
228 throw std::runtime_error("Unable to write to JSON file: " + outputPath.string());
229 }
230 file << root()->dump(indentSpaces.value_or(-1));
231 file.close();
232 }
233
238 void buildEntityMap(EntityMapPolicies policies = {},
239 const std::optional<ErrorHandler>& errorHandler = std::nullopt);
240
242 [[nodiscard]] const util::EntityMap& getEntityMap() const
243 {
244 MNX_ASSERT_IF(!m_entityMapping) {
245 throw std::logic_error("Call buildEntityMap before calling getEntityMap.");
246 }
247 return *m_entityMapping;
248 }
249
251 [[nodiscard]] bool hasEntityMap() const { return static_cast<bool>(m_entityMapping); }
252
256 [[nodiscard]] std::optional<Layout> findFullScoreLayout() const;
257};
258
259static_assert(std::is_move_constructible<mnx::Document>::value, "Document must be move constructible");
260
261} // namespace mnx
Represents an MNX array, encapsulating property access.
Definition BaseTypes.h:531
Base class wrapper for all MNX JSON nodes.
Definition BaseTypes.h:212
json_pointer pointer() const
Returns the json_pointer for this node.
Definition BaseTypes.h:274
T parent() const
Returns the parent object for this node.
Definition BaseTypes.h:260
const std::shared_ptr< json > & root() const
Returns the root.
Definition BaseTypes.h:297
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:134
void save(const std::filesystem::path &outputPath, std::optional< int > indentSpaces) const
Saves the MNX document to a file.
Definition Document.h:222
MNX_OPTIONAL_CHILD(Array< Layout >, layouts)
List of layouts for the MNX document.
MNX_OPTIONAL_CHILD(Array< Score >, scores)
List of scores for the MNX document.
MNX_REQUIRED_CHILD(Array< Part >, parts)
List of parts for the MNX document.
static Document create(const Byte *data, std::size_t size)
Creates a Document from a data buffer containing JSON.
Definition Document.h:207
static Document create(const std::filesystem::path &inputPath)
Creates a Document from a JSON file.
Definition Document.h:182
Document(const Document &src)
Copy constructor that zaps the id mapping, if any.
Definition Document.h:157
Document()
Constructs an empty MNX document. The resulting instance contains all required fields and should vali...
Definition Document.h:144
Document(const std::shared_ptr< json > &root)
Wrap a document around a root element.
Definition Document.h:154
const util::EntityMap & getEntityMap() const
Gets a reference to the entity mapping instance for the document.
Definition Document.h:242
Document(std::istream &inputStream)
Constructs a Document from an input stream.
Definition Document.h:165
bool hasEntityMap() const
Returns whether an entity mapping currently exists.
Definition Document.h:251
void buildEntityMap(EntityMapPolicies policies={}, const std::optional< ErrorHandler > &errorHandler=std::nullopt)
Builds or rebuilds the ID mapping for the document, replacing any existing mapping.
Definition Implementations.cpp:130
std::optional< Layout > findFullScoreLayout() const
Finds a layout that matches the canonical full score layout, where each part staff appears in order o...
Definition Implementations.cpp:372
MNX_REQUIRED_CHILD(MnxMetaData, mnx)
Metadata for the MNX document.
const std::shared_ptr< json > & root() const
Returns the root.
Definition BaseTypes.h:297
MNX_REQUIRED_CHILD(Global, global)
Global data for the MNX document.
Represents the global section of an MNX document, containing global measures.
Definition Global.h:405
Represents optional support metadata within an MNX document.
Definition Document.h:81
MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(bool, useBeams, false)
Optional property that indicates if beams are encoded.
MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(bool, useAccidentalDisplay, false)
Optional property indicating whether accidental display is used.
Represents metadata for an MNX document.
Definition Document.h:60
void clear_mnxdom()
Removes _x.mnxdom. If _x becomes empty, it is removed too.
Definition Document.h:120
MnxdomExtensionData ensure_mnxdom()
Ensures that _x.mnxdom exists and returns it.
Definition Document.h:109
std::optional< MnxdomExtensionData > mnxdom() const
Returns the mnxdom vendor extension stored in _x.mnxdom.
Definition Document.h:100
MNX_OPTIONAL_CHILD(Support, support)
Optional child containing support metadata.
MnxMetaData(Base &parent, std::string_view key)
Creates a new MnxMetaData class as a child of a JSON element.
Definition Document.h:67
MNX_REQUIRED_PROPERTY(int, version)
Required property indicating the version of the MNX document.
Provenance data stored under _x.mnxdom.
Definition MnxdomProvenance.h:125
Represents an MNX object, encapsulating property access.
Definition BaseTypes.h:429
void clearExtension(const std::string_view key)
Removes a vendor extension value from _x, if it exists.
Definition BaseTypes.h:475
Object()
Constructor fresh document or detached instance.
Definition BaseTypes.h:432
std::optional< json > getExtension(const std::string_view key) const
Gets a vendor extension value from _x.
Definition BaseTypes.h:463
Provides type-safe ID-based lookup for elements in an MNX document.
Definition EntityMap.h:109
object model for MNX format
nlohmann::json json
JSON class for MNX.
Definition BaseTypes.h:67
json::json_pointer json_pointer
JSON pointer class for MNX.
Definition BaseTypes.h:68
constexpr std::string_view MNXDOM_PROVENANCE_KEY
Key used for the mnxdom provenance payload inside _x.
Definition MnxdomProvenance.h:35
int getMnxSchemaVersion()
Returns the MNX version extracted from the trailing segment of schema $id.
Definition SchemaValidate.cpp:81
Controls optional behaviors when building an EntityMap.
Definition Document.h:48
bool ottavasRespectVoiceTargets
When false, ottava spans ignore voice-specific targeting (ottavas apply to the entire staff).
Definition Document.h:52
bool ottavasRespectGraceTargets
When false, ottava spans ignore grace-note targeting (graces follow the main event).
Definition Document.h:50