MNX Document Model
Loading...
Searching...
No Matches
BaseTypes.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 <memory>
25#include <unordered_map>
26#include <cassert>
27#include <iostream>
28#include <optional>
29#include <string>
30#include <string_view>
31#include <type_traits>
32#include <utility>
33
34// mnxdom provides enum (de)serialization; disable nlohmann's generic enum support.
35#if defined(NLOHMANN_JSON_HPP) && !defined(JSON_DISABLE_ENUM_SERIALIZATION)
36#error "nlohmann/json.hpp included before mnxdom without JSON_DISABLE_ENUM_SERIALIZATION=1"
37#endif
38
39#ifndef JSON_DISABLE_ENUM_SERIALIZATION
40#define JSON_DISABLE_ENUM_SERIALIZATION 1
41#endif
42
43#ifdef NLOHMANN_JSON_SYSTEM
44#include <nlohmann/json.hpp>
45#else
46#include "nlohmann/json.hpp"
47#endif
48#include "BoilerplateMacros.h"
49
56namespace mnx {
57
59[[nodiscard]] const std::string& getMnxSchemaId();
60
62[[nodiscard]] int getMnxSchemaVersion();
63
65inline const int MNX_VERSION = getMnxSchemaVersion();
66
67using json = nlohmann::json;
68using json_pointer = json::json_pointer;
69
70class Object;
71class Document;
72class Base;
73template <typename T> class Array;
74template <typename T> class Dictionary;
75
76namespace sequence {
77class Event;
78class Space;
80class Tuplet;
81} // namespace sequence
82
83namespace validation {
84class SemanticValidator;
85}; // namespace validation
86
87#ifndef DOXYGEN_SHOULD_IGNOR_THIS
88
89namespace scope {
90struct Default {};
92struct LayoutContent {};
93} // namespace scope
94
95#endif
96
97namespace detail {
98
100template <typename T, auto MakeFunc>
102
104template <typename T, typename R, typename... Args, R(*MakeFunc)(Args...)>
105struct ArrayAppendFromMake<T, MakeFunc>
106{
111 T append(Args... args)
112 {
113 return static_cast<Array<T>&>(*this).template appendImpl<T>(std::forward<Args>(args)...);
114 }
115};
116
118template <typename Derived, typename T>
120{
125 template <typename U = T, typename... Args,
126 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
127 U append(Args&&... args)
128 {
129 return static_cast<Derived&>(*this).template appendImpl<U>(std::forward<Args>(args)...);
130 }
131};
132
133template <typename Derived, typename T, typename = void>
134struct ArrayAppendOverloads : ArrayAppendBase<Derived, T> {};
135
136template <typename Derived, typename T>
137struct ArrayAppendOverloads<Derived, T, std::void_t<decltype(&T::make)>>
138 : ArrayAppendBase<Derived, T>,
139 ArrayAppendFromMake<T, &T::make>
140{
141 using ArrayAppendBase<Derived, T>::append;
142 using ArrayAppendFromMake<T, &T::make>::append;
143};
144
146template <typename T, auto MakeFunc>
148
150template <typename T, typename R, typename... Args, R(*MakeFunc)(Args...)>
151struct DictionaryAppendFromMake<T, MakeFunc>
152{
157 T append(std::string_view key, Args... args)
158 {
159 return static_cast<Dictionary<T>&>(*this).template appendImpl<T>(key, std::forward<Args>(args)...);
160 }
161};
162
164template <typename Derived, typename T>
166{
171 template <typename U = T, typename... Args,
172 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
173 U append(std::string_view key, Args&&... args)
174 {
175 return static_cast<Derived&>(*this).template appendImpl<U>(key, std::forward<Args>(args)...);
176 }
177};
178
179template <typename Derived, typename T, typename = void>
181
182template <typename Derived, typename T>
183struct DictionaryAppendOverloads<Derived, T, std::void_t<decltype(&T::make)>>
184 : DictionaryAppendBase<Derived, T>,
185 DictionaryAppendFromMake<T, &T::make>
186{
187 using DictionaryAppendBase<Derived, T>::append;
188 using DictionaryAppendFromMake<T, &T::make>::append;
189};
190
191} // namespace detail
192
193
197class Base
198{
199public:
200 virtual ~Base() = default;
201
203 Base(const Base& src) : m_root(src.m_root), m_pointer(src.m_pointer)
204 {}
205
207 Base(Base&& src) noexcept : m_root(src.m_root), // m_root must be copied (not moved)
208 m_pointer(std::move(src.m_pointer))
209 {}
210
212 Base& operator=(const Base& src)
213 {
214 if (this != &src) {
215 if (m_root != src.m_root) {
216 throw std::logic_error("Assignment from a different JSON document is not allowed.");
217 }
218 m_pointer = src.m_pointer;
219 }
220 return *this;
221 }
222
225 {
226 if (this != &src) {
227 if (m_root != src.m_root) {
228 throw std::logic_error("Assignment from a different JSON document is not allowed.");
229 }
230 m_pointer = std::move(src.m_pointer);
231 }
232 return *this;
233 }
234
237 [[nodiscard]] std::string dump(int indents = -1) const
238 {
239 return ref().dump(indents);
240 }
241
245 template <typename T>
246 [[nodiscard]] T parent() const
247 {
248 static_assert(std::is_base_of_v<Base, T>, "Template type mush be derived from Base.");
249 return T(m_root, m_pointer.parent_pointer());
250 }
251
256 template <typename T, typename Scope = scope::Default>
257 [[nodiscard]] std::optional<T> getEnclosingElement() const;
258
260 [[nodiscard]] json_pointer pointer() const { return m_pointer; }
261
263 [[nodiscard]] Document document() const;
264
266 [[nodiscard]] bool empty() const { return ref().empty(); }
267
268protected:
274 [[nodiscard]] json& ref() const { return resolve_pointer(); }
275
280 [[nodiscard]] json& ref() { return resolve_pointer(); }
281
283 [[nodiscard]] const std::shared_ptr<json>& root() const { return m_root; }
284
290 Base(const std::shared_ptr<json>& root, json_pointer pointer)
291 : m_root(root), m_pointer(std::move(pointer)) {}
292
299 Base(json&& jsonRef, Base& parent, std::string_view key)
300 : m_root(parent.m_root), m_pointer(parent.m_pointer / std::string(key))
301 {
302 (*m_root)[m_pointer] = std::move(jsonRef);
303 }
304
312 template <typename T>
313 [[nodiscard]] T getChild(std::string_view key) const
314 {
315 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
316
317 json_pointer childPointer = m_pointer / std::string(key);
318 if (!checkKeyIsValid<T>(childPointer)) {
319 throw std::out_of_range("Child node not found: " + std::string(key));
320 }
321
322 return T(m_root, childPointer);
323 }
324
332 template <typename T>
333 T setChild(std::string_view key, const T& value)
334 {
335 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
336
337 json_pointer childPointer = m_pointer / std::string(key);
338 (*m_root)[childPointer] = value.ref();
339 return T(m_root, childPointer);
340 }
341
349 template <typename T>
350 [[nodiscard]] std::optional<T> getOptionalChild(std::string_view key) const
351 {
352 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
353
354 json_pointer childPointer = m_pointer / std::string(key);
355 if (!checkKeyIsValid<T>(childPointer)) {
356 return std::nullopt;
357 }
358
359 return T(m_root, childPointer);
360 }
361
362private:
370 template <typename T>
371 [[nodiscard]] bool checkKeyIsValid(const json_pointer& pointer) const
372 {
373 if (!(*m_root).contains(pointer)) {
374 return false;
375 }
376
377 const json& node = (*m_root).at(pointer);
378
379 if constexpr (std::is_base_of_v<Object, T>) {
380 if (!node.is_object()) {
381 throw std::runtime_error("Expected an object for: " + pointer.to_string());
382 }
383 } else if constexpr (std::is_base_of_v<Array<typename T::value_type>, T>) {
384 if (!node.is_array()) {
385 throw std::runtime_error("Expected an array for: " + pointer.to_string());
386 }
387 }
388
389 return true;
390 }
391
397 [[nodiscard]] json& resolve_pointer() const
398 {
399 return (*m_root).at(m_pointer); // Throws if invalid
400 }
401
402 const std::shared_ptr<json> m_root;
403 json_pointer m_pointer;
404
405 friend class validation::SemanticValidator;
406};
407
409using ErrorHandler = std::function<void(const std::string& message, const Base& location)>;
410
414class Object : public Base
415{
416public:
418 Object() : Object(std::make_shared<json>(json::object()), json_pointer{})
419 {}
420
424 Object(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
425 {
426 if (!ref().is_object()) {
427 throw std::invalid_argument("mnx::Object must wrap a JSON object.");
428 }
429 }
430
434 Object(Base& parent, std::string_view key)
435 : Base(json::object(), parent, key) {}
436
437 MNX_OPTIONAL_PROPERTY(std::string, _c);
439 MNX_OPTIONAL_PROPERTY(std::string, id);
440
442 void setExtension(const std::string& key, const json& value)
443 {
444 auto extensions = ensure__x();
445 extensions.ref()[key] = value;
446 }
447
449 [[nodiscard]] std::optional<json> getExtension(const std::string& key) const
450 {
451 if (auto extensions = _x()) {
452 const auto it = extensions->ref().find(key);
453 if (it != extensions->ref().end()) {
454 return *it;
455 }
456 }
457 return std::nullopt;
458 }
459};
460
463template <typename T, std::enable_if_t<!std::is_base_of_v<Base, T>, int> = 0>
464class SimpleType : public Base
465{
466 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>, "This template is for simple JSON classes");
467
468public:
469 using value_type = T;
470
474 SimpleType(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
475 {
476 }
477
479 operator T() const
480 {
481 return ref().template get<T>();
482 }
483
486 SimpleType& operator=(const T& src)
487 {
488 ref() = src;
489 return *this;
490 }
491
493 [[nodiscard]] bool operator==(const T& src) const
494 {
495 return src == ref().template get<T>();
496 }
497};
498
499class ArrayElementObject;
503template <typename T>
504class Array : public Base,
505 public detail::ArrayAppendOverloads<Array<T>, T>
506{
507 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
508 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX array element type.");
509
510private:
511 template<typename ArrayType>
512 struct iter
513 {
514 private:
515 ArrayType* m_ptr;
516 mutable size_t m_idx;
517
518 public:
519 using iterator_category = std::input_iterator_tag;
520 using value_type = T;
521 using difference_type = std::ptrdiff_t;
522 using pointer = void; // not meaningful for proxy/value-returning iterators
523 using reference = T; // since operator* returns by value
524
525 iter(ArrayType* ptr, size_t idx) : m_ptr(ptr), m_idx(idx) {}
526 T operator*() const { return (*m_ptr)[m_idx]; }
527 iter& operator++() { ++m_idx; return *this; }
528 bool operator!=(const iter& o) const { return m_idx != o.m_idx; }
529 };
530
531public:
533 using value_type = T;
534
535 using iterator = iter<Array>;
536 using const_iterator = iter<const Array>;
537
539 Array() : Array(std::make_shared<json>(json::array()), json_pointer{})
540 {}
541
545 Array(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
546 {
547 if (!ref().is_array()) {
548 throw std::invalid_argument("mnx::Array must wrap a JSON array.");
549 }
550 }
551
555 Array(Base& parent, std::string_view key)
556 : Base(json::array(), parent, key) {}
557
559 [[nodiscard]] size_t size() const { return ref().size(); }
560
562 [[nodiscard]] bool empty() const { return ref().empty(); }
563
565 void clear() { ref().clear(); }
566
568 [[nodiscard]] T at(size_t index) const
569 {
570 return operator[](index);
571 }
572
576 [[nodiscard]] T front() const { return (*this)[0]; }
577
581 [[nodiscard]] T back() const { return (*this)[size() - 1]; }
582
584 [[nodiscard]] auto operator[](size_t index) const
585 {
586 checkIndex(index);
587 if constexpr (std::is_base_of_v<Base, T>) {
588 return getChild<T>(std::to_string(index));
589 } else {
590 return getChild<SimpleType<T>>(std::to_string(index));
591 }
592 }
593
595 [[nodiscard]] auto operator[](size_t index)
596 {
597 checkIndex(index);
598 if constexpr (std::is_base_of_v<Base, T>) {
599 return getChild<T>(std::to_string(index));
600 } else {
601 return getChild<SimpleType<T>>(std::to_string(index));
602 }
603 }
604
606 template <typename U = T>
607 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
608 push_back(const U& value)
609 {
610 ref().push_back(value);
611 }
612
614 void erase(size_t index)
615 {
616 checkIndex(index);
617 ref().erase(ref().begin() + index);
618 }
619
624 template <typename U = T>
625 std::enable_if_t<!std::is_base_of_v<Base, U>, std::vector<U>>
627 { return std::vector<U>(begin(), end()); }
628
630 [[nodiscard]] auto begin() { return iterator(this, 0); }
631
633 [[nodiscard]] auto end() { return iterator(this, size()); }
634
636 [[nodiscard]] auto begin() const { return const_iterator(this, 0); }
637
639 [[nodiscard]] auto end() const { return const_iterator(this, size()); }
640
641protected:
644 void checkIndex(size_t index) const
645 {
646 assert(index < ref().size());
647 if (index >= ref().size()) {
648 throw std::out_of_range("Index out of range");
649 }
650 }
651
652private:
653 template <typename U, typename... Args>
654 U appendImpl(Args&&... args)
655 {
656 static_assert(std::is_base_of_v<Base, U>, "Array::appendImpl requires a Base-derived element type.");
657 if constexpr (std::is_base_of_v<Object, U>) {
658 ref().push_back(json::object());
659 } else {
660 ref().push_back(json::array());
661 }
662 return U(*this, std::to_string(ref().size() - 1), std::forward<Args>(args)...);
663 }
664
665 template <typename, auto>
666 friend struct detail::ArrayAppendFromMake;
667 template <typename, typename>
668 friend struct detail::ArrayAppendBase;
669};
670
675{
676public:
677 using Object::Object;
678
680 size_t calcArrayIndex() const
681 {
682 return std::stoul(pointer().back());
683 }
684
689 template <typename ContainerType>
690 ContainerType container() const;
691};
692
693class ContentArray;
696{
697protected:
698 virtual std::string_view defaultType() const;
699
700public:
701 using ArrayElementObject::ArrayElementObject;
702
703 MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(std::string, type, std::string(defaultType()));
704
706 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
707 [[nodiscard]] T get() const
708 {
709 return getTypedObject<T>();
710 }
711
714 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
715 [[nodiscard]] T getTypedObject() const
716 {
717 if (type() != T::ContentTypeValue) {
718 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
719 ", got " + type());
720 }
721 return T(root(), pointer());
722 }
723
724 friend class ContentArray;
725};
726
727template <typename ContainerType>
728inline ContainerType ArrayElementObject::container() const
729{
730 if constexpr (std::is_base_of_v<ContainerType, ContentObject>) {
731 const auto obj = parent<Array<ArrayElementObject>>().template parent<ContentObject>();
732 if constexpr (std::is_same_v<ContainerType, ContentObject>) {
733 return obj;
734 } else {
735 MNX_ASSERT_IF(obj.type() != ContainerType::ContentTypeValue) {
736 throw std::invalid_argument(
737 "container(): requested type does not match underlying content object type");
738 }
739 return obj.template get<ContainerType>();
740 }
741 } else {
742 return parent<Array<ArrayElementObject>>().template parent<ContainerType>();
743 }
744}
745
773class ContentArray : public Array<ContentObject>
774{
775public:
777 using BaseArray::BaseArray; // Inherit constructors
778
780 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
781 [[nodiscard]] T get(size_t index) const
782 {
783 this->checkIndex(index);
784 return operator[](index).get<T>();
785 }
786
788 template <typename T,
789 std::enable_if_t<std::is_base_of_v<ContentObject, T> &&
790 !std::is_same_v<T, sequence::Event> &&
791 !std::is_same_v<T, sequence::Space> &&
792 !std::is_same_v<T, sequence::MultiNoteTremolo> &&
793 !std::is_same_v<T, sequence::Tuplet>, int> = 0>
795 {
796 return appendWithType<T>();
797 }
798
800 template <typename T, typename... Args,
801 std::enable_if_t<std::is_base_of_v<ContentObject, T> && (sizeof...(Args) > 0), int> = 0>
802 T append(const Args&... args)
803 {
804 static_assert(!std::is_same_v<T, T>,
805 "ContentArray::append requires explicit specialization for each content type.");
806 return appendWithType<T>(args...);
807 }
808
809 // Prevent untemplated append() calls; callers must use append<T>(...).
810 ContentObject append(...) = delete;
811
812private:
813 template <typename T, typename... Args>
814 T appendWithType(Args&&... args)
815 {
816 static_assert(std::is_base_of_v<ContentObject, T>,
817 "ContentArray::appendWithType requires a ContentObject-derived type.");
818 auto result = BaseArray::append<T>(std::forward<Args>(args)...);
819 const ContentObject& contentObject = result;
820 if (T::ContentTypeValue != contentObject.defaultType()) {
821 result.set_type(std::string(T::ContentTypeValue));
822 }
823 return result;
824 }
825
828 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
829 [[nodiscard]] T getTypedObject(size_t index) const
830 {
831 this->checkIndex(index);
832 auto element = (*this)[index];
833 if (element.type() != T::ContentTypeValue) {
834 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
835 ", got " + element.type());
836 }
837 return T(root(), pointer() / std::to_string(index));
838 }
839};
840
845template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
847{
848 static const std::unordered_map<std::string, E> stringToEnum();
849
851 static const std::unordered_map<E, std::string> enumToString()
852 {
853 static const std::unordered_map<E, std::string> reverseMap = []() {
854 std::unordered_map<E, std::string> result;
855 for (const auto& element : EnumStringMapping<E>::stringToEnum()) {
856 result.emplace(element.second, element.first);
857 }
858 return result;
859 }();
860 return reverseMap;
861 }
862};
863
867template <typename T>
868class Dictionary : public Object,
869 public detail::DictionaryAppendOverloads<Dictionary<T>, T>
870{
871 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
872 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX dictionary element type.");
873
874private:
875 template <typename DictionaryType, typename IteratorType>
876 struct iter
877 {
878 using value_type = std::pair<const std::string, T>;
879 using difference_type = std::ptrdiff_t;
880 using iterator_category = std::forward_iterator_tag;
881 using pointer = value_type*;
882 using reference = value_type&;
883
884 private:
885 DictionaryType* m_ptr {};
886 IteratorType m_it {};
887 mutable std::optional<value_type> m_pair; // cached key/value
888
889 void update_pair() const
890 {
891 m_pair.reset();
892 if (m_it != m_ptr->ref().end()) {
893 m_pair.emplace(m_it.key(), m_ptr->valueForKey(m_it.key()));
894 }
895 }
896
897 public:
898 iter(DictionaryType* ptr, IteratorType it)
899 : m_ptr(ptr), m_it(it)
900 {
901 update_pair();
902 }
903
904 [[nodiscard]] reference operator*() const { return *m_pair; }
905 [[nodiscard]] pointer operator->() const { return &*m_pair; }
906
907 iter& operator++() { ++m_it; update_pair(); return *this; }
908 iter operator++(int) { iter tmp(*this); ++(*this); return tmp; }
909
910 [[nodiscard]] bool operator!=(const iter& o) const { return m_it != o.m_it; }
911 [[nodiscard]] bool operator==(const iter& o) const { return m_it == o.m_it; }
912 };
913
914public:
916 using value_type = T;
917
918 using iterator = iter<Dictionary, json::iterator>;
919 using const_iterator = iter<const Dictionary, json::const_iterator>;
920
924 Dictionary(const std::shared_ptr<json>& root, json_pointer pointer)
926 {
927 }
928
932 Dictionary(Base& parent, std::string_view key)
933 : Object(parent, key) {}
934
936 [[nodiscard]] size_t size() const { return ref().size(); }
937
939 [[nodiscard]] bool empty() const { return ref().empty(); }
940
942 void clear() { ref().clear(); }
943
947 [[nodiscard]] T at(std::string_view key) const
948 { return valueForKey(key); }
949
951 template <typename U = T>
952 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
953 emplace(std::string_view key, const U& value)
954 {
955 ref()[key] = value;
956 }
957
959 void erase(std::string_view key)
960 {
961 ref().erase(key);
962 }
963
967 [[nodiscard]] auto find(std::string_view key)
968 {
969 auto it = ref().find(key);
970 return (it != ref().end()) ? iterator(this, it) : end();
971 }
972
976 [[nodiscard]] auto find(std::string_view key) const
977 {
978 auto it = ref().find(key);
979 return (it != ref().end()) ? const_iterator(this, it) : end();
980 }
981
984 [[nodiscard]] bool contains(std::string_view key) const
985 { return find(key) != end(); }
986
988 [[nodiscard]] auto begin() { return iterator(this, ref().begin()); }
989
991 [[nodiscard]] auto end() { return iterator(this, ref().end()); }
992
994 [[nodiscard]] auto begin() const { return const_iterator(this, ref().begin()); }
995
997 [[nodiscard]] auto end() const { return const_iterator(this, ref().end()); }
998
999private:
1001 [[nodiscard]] T valueForKey(std::string_view key) const
1002 {
1003 if constexpr (std::is_base_of_v<Base, T>) {
1004 return getChild<T>(key);
1005 } else {
1006 return getChild<SimpleType<T>>(key);
1007 }
1008 }
1009
1010 template <typename U, typename... Args>
1011 U appendImpl(std::string_view key, Args&&... args)
1012 {
1013 static_assert(std::is_base_of_v<Base, U>, "Dictionary::appendImpl requires a Base-derived element type.");
1014 if constexpr (std::is_base_of_v<Object, U>) {
1015 ref()[key] = json::object();
1016 } else {
1017 ref()[key] = json::array();
1018 }
1019 return U(*this, key, std::forward<Args>(args)...);
1020 }
1021
1022 template <typename, auto>
1023 friend struct detail::DictionaryAppendFromMake;
1024 template <typename, typename>
1025 friend struct detail::DictionaryAppendBase;
1026};
1027
1028} // namespace mnx
1029
1030#ifndef DOXYGEN_SHOULD_IGNORE_THIS
1031
1032namespace nlohmann {
1033
1034#if defined(_WIN32)
1035// This general adl_serializer is enabled only for enum types.
1036// For some reason MSC does not like the direct function definitions below.
1037template<typename EnumType>
1038struct adl_serializer<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
1039{
1040 template<typename BasicJsonType>
1041 static EnumType from_json(const BasicJsonType& j)
1042 {
1043 // Lookup the string in the specialized map.
1045 auto it = map.find(j.get<std::string>());
1046 if (it != map.end()) {
1047 return it->second;
1048 }
1050 return EnumType{};
1051 }
1052
1053 template<typename BasicJsonType>
1054 static void to_json(BasicJsonType& j, const EnumType& value)
1055 {
1057 auto it = map.find(value);
1058 if (it == map.end()) {
1060 j = BasicJsonType();
1061 return;
1062 }
1063 j = it->second;
1064 }
1065};
1066#else
1067// Clang works with the adl_specialization above, but GCC does not.
1068namespace detail {
1069
1070template<typename BasicJsonType, typename EnumType,
1071 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1072inline void from_json(const BasicJsonType& j, EnumType& value)
1073{
1074 // Lookup the string in the specialized map.
1076 auto it = map.find(j.template get<std::string>());
1077 if (it != map.end()) {
1078 value = it->second;
1079 } else {
1081 value = EnumType{};
1082 }
1083}
1084
1085template<typename BasicJsonType, typename EnumType,
1086 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1087inline void to_json(BasicJsonType& j, EnumType value) noexcept
1088{
1090 auto it = map.find(value);
1091 if (it != map.end()) {
1092 j = it->second;
1093 } else {
1095 j = BasicJsonType();
1096 }
1097}
1098
1099} // namespace detail
1100#endif // defined(_WIN32)
1101
1102} // namespace nlohmann
1103
1104#endif // DOXYGEN_SHOULD_IGNORE_THIS
Represents an MNX object that is included as an array element.
Definition BaseTypes.h:675
ContainerType container() const
Returns the object that owns the content array this element belongs to wrapped as the specified templ...
Definition BaseTypes.h:728
size_t calcArrayIndex() const
Calculates the array index of the current instance within the array.
Definition BaseTypes.h:680
Represents an MNX array, encapsulating property access.
Definition BaseTypes.h:506
T front() const
Access the first element.
Definition BaseTypes.h:576
void checkIndex(size_t index) const
validates that an index is not out of range
Definition BaseTypes.h:644
auto begin()
Returns an iterator to the beginning of the array.
Definition BaseTypes.h:630
size_t size() const
Get the size of the array.
Definition BaseTypes.h:559
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:562
iter< Array > iterator
non-const iterator type
Definition BaseTypes.h:535
iter< const Array > const_iterator
const iterator type
Definition BaseTypes.h:536
T value_type
The type for elements in this Array.
Definition BaseTypes.h:533
auto operator[](size_t index) const
const operator[]
Definition BaseTypes.h:584
Array(Base &parent, std::string_view key)
Creates a new Array class as a child of a JSON node.
Definition BaseTypes.h:555
auto begin() const
Returns a const iterator to the beginning of the array.
Definition BaseTypes.h:636
void erase(size_t index)
Remove an element at a given index.
Definition BaseTypes.h:614
std::enable_if_t<!std::is_base_of_v< Base, U >, std::vector< U > > toStdVector() const
Converts the Array to an owning std::vector.
Definition BaseTypes.h:626
auto end()
Returns an iterator to the end of the array.
Definition BaseTypes.h:633
T back() const
Access the last element.
Definition BaseTypes.h:581
Array()
Constructor for detached array instance.
Definition BaseTypes.h:539
void clear()
Clear all elements.
Definition BaseTypes.h:565
Array(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Array class around an existing JSON array node.
Definition BaseTypes.h:545
std::enable_if_t<!std::is_base_of_v< Base, U >, void > push_back(const U &value)
Append a new value to the array. (Available only for primitive types)
Definition BaseTypes.h:608
T at(size_t index) const
Direct getter for a particular element.
Definition BaseTypes.h:568
auto end() const
Returns a const iterator to the end of the array.
Definition BaseTypes.h:639
auto operator[](size_t index)
non-const operator[]
Definition BaseTypes.h:595
Base class wrapper for all MNX JSON nodes.
Definition BaseTypes.h:198
json & ref() const
Convert this node for retrieval.
Definition BaseTypes.h:274
Base(json &&jsonRef, Base &parent, std::string_view key)
Construct a Base reference as a child inside a parent node.
Definition BaseTypes.h:299
json_pointer pointer() const
Returns the json_pointer for this node.
Definition BaseTypes.h:260
Base(const std::shared_ptr< json > &root, json_pointer pointer)
Wrap a Base instance around a specific JSON reference using a json_pointer.
Definition BaseTypes.h:290
T parent() const
Returns the parent object for this node.
Definition BaseTypes.h:246
T getChild(std::string_view key) const
Retrieves and validates a required child node.
Definition BaseTypes.h:313
json & ref()
Access the JSON node for modification.
Definition BaseTypes.h:280
std::optional< T > getOptionalChild(std::string_view key) const
Retrieves an optional child node.
Definition BaseTypes.h:350
std::optional< T > getEnclosingElement() const
Returns the enclosing array element for this instance. If T is a type that can be nested (e....
Definition Implementations.cpp:69
T setChild(std::string_view key, const T &value)
Sets a child node.
Definition BaseTypes.h:333
bool empty() const
Returns true if the object is empty.
Definition BaseTypes.h:266
Base(const Base &src)
Copy constructor.
Definition BaseTypes.h:203
Base & operator=(const Base &src)
Copy assignment operator.
Definition BaseTypes.h:212
Base(Base &&src) noexcept
Move constructor.
Definition BaseTypes.h:207
Base & operator=(Base &&src)
Move assignment operator.
Definition BaseTypes.h:224
std::string dump(int indents=-1) const
Dumps the branch to a string. Useful in debugging.
Definition BaseTypes.h:237
Document document() const
Returns the document root.
Definition Implementations.cpp:120
const std::shared_ptr< json > & root() const
Returns the root.
Definition BaseTypes.h:283
Class for content arrays.
Definition BaseTypes.h:774
T get(size_t index) const
Retrieve an element from the array as a specific type.
Definition BaseTypes.h:781
T append()
Append an element of the specified type (default overload for no-arg content types).
Definition BaseTypes.h:794
T append(const Args &... args)
Append overload entry point for explicitly specialized argful content types.
Definition BaseTypes.h:802
Base class for objects that are elements of content arrays.
Definition BaseTypes.h:696
MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(std::string, type, std::string(defaultType()))
determines our type in the JSON
T get() const
Retrieve an element as a specific type.
Definition BaseTypes.h:707
T getTypedObject() const
Constructs an object of type T if its type matches the JSON type.
Definition BaseTypes.h:715
Represents an MNX dictionary, where each key is a user-defined string.
Definition BaseTypes.h:870
iter< Dictionary, json::iterator > iterator
non-const iterator type
Definition BaseTypes.h:918
size_t size() const
Get the size of the array.
Definition BaseTypes.h:936
auto end() const
Returns a const iterator to the end of the dictionary.
Definition BaseTypes.h:997
auto begin() const
Returns a const iterator to the beginning of the dictionary.
Definition BaseTypes.h:994
std::enable_if_t<!std::is_base_of_v< Base, U >, void > emplace(std::string_view key, const U &value)
Add a new value to the dictonary. (Available only for primitive types)
Definition BaseTypes.h:953
auto find(std::string_view key)
Finds an element by key and returns an iterator.
Definition BaseTypes.h:967
Dictionary(Base &parent, std::string_view key)
Creates a new Dictionary class as a child of a JSON node.
Definition BaseTypes.h:932
iter< const Dictionary, json::const_iterator > const_iterator
const iterator type
Definition BaseTypes.h:919
T value_type
The type for elements in this Array.
Definition BaseTypes.h:916
auto begin()
Returns an iterator to the beginning of the dictionary.
Definition BaseTypes.h:988
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:939
auto find(std::string_view key) const
Finds an element by key and returns a const iterator.
Definition BaseTypes.h:976
void erase(std::string_view key)
Remove an element at a given key.
Definition BaseTypes.h:959
Dictionary(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Dictionary class around an existing JSON node.
Definition BaseTypes.h:924
T at(std::string_view key) const
Direct getter for a particular element.
Definition BaseTypes.h:947
auto end()
Returns an iterator to the end of the dictionary.
Definition BaseTypes.h:991
bool contains(std::string_view key) const
Returns true if the key exists in in the dictionary.
Definition BaseTypes.h:984
void clear()
Clear all elements.
Definition BaseTypes.h:942
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:103
Represents an MNX object, encapsulating property access.
Definition BaseTypes.h:415
MNX_OPTIONAL_PROPERTY(std::string, _c)
An optional comment. This serves a similar function as XML or HTML comments.
Object(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Object class around an existing JSON object node.
Definition BaseTypes.h:424
Object()
Constructor fresh document or detached instance.
Definition BaseTypes.h:418
MNX_OPTIONAL_CHILD(Object, _x)
Vendor-defined dictionary.
void setExtension(const std::string &key, const json &value)
Sets a vendor extension value in _x, creating _x when needed.
Definition BaseTypes.h:442
std::optional< json > getExtension(const std::string &key) const
Gets a vendor extension value from _x.
Definition BaseTypes.h:449
MNX_OPTIONAL_PROPERTY(std::string, id)
Uniquely identifies the object.
Object(Base &parent, std::string_view key)
Creates a new Object class as a child of a JSON node.
Definition BaseTypes.h:434
Allows access to a fundamental type (number, boolean, string) in a JSON node.
Definition BaseTypes.h:465
SimpleType(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps a SimpleType class around an existing JSON object node.
Definition BaseTypes.h:474
bool operator==(const T &src) const
Equality comparison with value type.
Definition BaseTypes.h:493
T value_type
value type of this SimpleType
Definition BaseTypes.h:469
SimpleType & operator=(const T &src)
Allow assignment to underlying json reference.
Definition BaseTypes.h:486
Represents a musical event within a sequence.
Definition Sequence.h:418
Represents a multi-note tremolo sequence within a sequence.
Definition Sequence.h:565
Occupies metric space without showing anything.
Definition Sequence.h:493
Represents a tuplet sequence within a sequence.
Definition Sequence.h:616
object model for MNX format
nlohmann::json json
JSON class for MNX.
Definition BaseTypes.h:67
std::function< void(const std::string &message, const Base &location)> ErrorHandler
Error handler type for reporting errors.
Definition BaseTypes.h:409
json::json_pointer json_pointer
JSON pointer class for MNX.
Definition BaseTypes.h:68
const int MNX_VERSION
The MNX version for files generated by the DOM.
Definition BaseTypes.h:65
const std::string & getMnxSchemaId()
Returns the MNX schema id from the embedded schema.
Definition SchemaValidate.cpp:76
int getMnxSchemaVersion()
Returns the MNX version extracted from the trailing segment of schema $id.
Definition SchemaValidate.cpp:81
Supplies enum string mappings to nlohmann json's serializer.
Definition BaseTypes.h:847
static const std::unordered_map< E, std::string > enumToString()
maps enum values to strings
Definition BaseTypes.h:851
static const std::unordered_map< std::string, E > stringToEnum()
maps strings to enum values
Base append implementation for arrays of Base-derived types.
Definition BaseTypes.h:120
U append(Args &&... args)
Append a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:127
T append(Args... args)
Append a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:111
Adds an append(...) overload that mirrors a type's static make(...) signature for arrays.
Definition BaseTypes.h:101
Definition BaseTypes.h:134
Base append implementation for dictionaries of Base-derived types.
Definition BaseTypes.h:166
U append(std::string_view key, Args &&... args)
Append a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:173
T append(std::string_view key, Args... args)
Create a new element using the input key. (Available only for Base types)
Definition BaseTypes.h:157
Adds an append(key, ...) overload that mirrors a type's static make(...) signature for dictionaries.
Definition BaseTypes.h:147
Definition BaseTypes.h:180
Definition BaseTypes.h:90
Definition BaseTypes.h:92
Definition BaseTypes.h:91