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
265protected:
271 [[nodiscard]] json& ref() const { return resolve_pointer(); }
272
277 [[nodiscard]] json& ref() { return resolve_pointer(); }
278
280 [[nodiscard]] const std::shared_ptr<json>& root() const { return m_root; }
281
287 Base(const std::shared_ptr<json>& root, json_pointer pointer)
288 : m_root(root), m_pointer(std::move(pointer)) {}
289
296 Base(json&& jsonRef, Base& parent, std::string_view key)
297 : m_root(parent.m_root), m_pointer(parent.m_pointer / std::string(key))
298 {
299 (*m_root)[m_pointer] = std::move(jsonRef);
300 }
301
309 template <typename T>
310 [[nodiscard]] T getChild(std::string_view key) const
311 {
312 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
313
314 json_pointer childPointer = m_pointer / std::string(key);
315 if (!checkKeyIsValid<T>(childPointer)) {
316 throw std::out_of_range("Child node not found: " + std::string(key));
317 }
318
319 return T(m_root, childPointer);
320 }
321
329 template <typename T>
330 T setChild(std::string_view key, const T& value)
331 {
332 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
333
334 json_pointer childPointer = m_pointer / std::string(key);
335 (*m_root)[childPointer] = value.ref();
336 return T(m_root, childPointer);
337 }
338
346 template <typename T>
347 [[nodiscard]] std::optional<T> getOptionalChild(std::string_view key) const
348 {
349 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
350
351 json_pointer childPointer = m_pointer / std::string(key);
352 if (!checkKeyIsValid<T>(childPointer)) {
353 return std::nullopt;
354 }
355
356 return T(m_root, childPointer);
357 }
358
359private:
367 template <typename T>
368 [[nodiscard]] bool checkKeyIsValid(const json_pointer& pointer) const
369 {
370 if (!(*m_root).contains(pointer)) {
371 return false;
372 }
373
374 const json& node = (*m_root).at(pointer);
375
376 if constexpr (std::is_base_of_v<Object, T>) {
377 if (!node.is_object()) {
378 throw std::runtime_error("Expected an object for: " + pointer.to_string());
379 }
380 } else if constexpr (std::is_base_of_v<Array<typename T::value_type>, T>) {
381 if (!node.is_array()) {
382 throw std::runtime_error("Expected an array for: " + pointer.to_string());
383 }
384 }
385
386 return true;
387 }
388
394 [[nodiscard]] json& resolve_pointer() const
395 {
396 return (*m_root).at(m_pointer); // Throws if invalid
397 }
398
399 const std::shared_ptr<json> m_root;
400 json_pointer m_pointer;
401
402 friend class validation::SemanticValidator;
403};
404
406using ErrorHandler = std::function<void(const std::string& message, const Base& location)>;
407
411class Object : public Base
412{
413public:
417 Object(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
418 {
419 if (!ref().is_object()) {
420 throw std::invalid_argument("mnx::Object must wrap a JSON object.");
421 }
422 }
423
427 Object(Base& parent, std::string_view key)
428 : Base(json::object(), parent, key) {}
429
430 MNX_OPTIONAL_PROPERTY(std::string, _c);
432 MNX_OPTIONAL_PROPERTY(std::string, id);
433
435 void setExtension(const std::string& key, const json& value)
436 {
437 auto extensions = ensure__x();
438 extensions.ref()[key] = value;
439 }
440
442 [[nodiscard]] std::optional<json> getExtension(const std::string& key) const
443 {
444 if (auto extensions = _x()) {
445 const auto it = extensions->ref().find(key);
446 if (it != extensions->ref().end()) {
447 return *it;
448 }
449 }
450 return std::nullopt;
451 }
452};
453
456template <typename T, std::enable_if_t<!std::is_base_of_v<Base, T>, int> = 0>
457class SimpleType : public Base
458{
459 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>, "This template is for simple JSON classes");
460
461public:
462 using value_type = T;
463
467 SimpleType(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
468 {
469 }
470
472 operator T() const
473 {
474 return ref().template get<T>();
475 }
476
479 SimpleType& operator=(const T& src)
480 {
481 ref() = src;
482 return *this;
483 }
484
486 bool operator==(const T& src) const
487 {
488 return src == ref().template get<T>();
489 }
490};
491
492class ArrayElementObject;
496template <typename T>
497class Array : public Base,
498 public detail::ArrayAppendOverloads<Array<T>, T>
499{
500 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
501 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX array element type.");
502
503private:
504 template<typename ArrayType>
505 struct iter
506 {
507 private:
508 ArrayType* m_ptr;
509 mutable size_t m_idx;
510
511 public:
512 using iterator_category = std::input_iterator_tag;
513 using value_type = T;
514 using difference_type = std::ptrdiff_t;
515 using pointer = void; // not meaningful for proxy/value-returning iterators
516 using reference = T; // since operator* returns by value
517
518 iter(ArrayType* ptr, size_t idx) : m_ptr(ptr), m_idx(idx) {}
519 T operator*() const { return (*m_ptr)[m_idx]; }
520 iter& operator++() { ++m_idx; return *this; }
521 bool operator!=(const iter& o) const { return m_idx != o.m_idx; }
522 };
523
524public:
526 using value_type = T;
527
528 using iterator = iter<Array>;
529 using const_iterator = iter<const Array>;
530
534 Array(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
535 {
536 if (!ref().is_array()) {
537 throw std::invalid_argument("mnx::Array must wrap a JSON array.");
538 }
539 }
540
544 Array(Base& parent, std::string_view key)
545 : Base(json::array(), parent, key) {}
546
548 [[nodiscard]] size_t size() const { return ref().size(); }
549
551 [[nodiscard]] bool empty() const { return ref().empty(); }
552
554 void clear() { ref().clear(); }
555
557 [[nodiscard]] T at(size_t index) const
558 {
559 return operator[](index);
560 }
561
565 [[nodiscard]] T front() const { return (*this)[0]; }
566
570 [[nodiscard]] T back() const { return (*this)[size() - 1]; }
571
573 [[nodiscard]] auto operator[](size_t index) const
574 {
575 checkIndex(index);
576 if constexpr (std::is_base_of_v<Base, T>) {
577 return getChild<T>(std::to_string(index));
578 } else {
579 return getChild<SimpleType<T>>(std::to_string(index));
580 }
581 }
582
584 [[nodiscard]] auto operator[](size_t index)
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 template <typename U = T>
596 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
597 push_back(const U& value)
598 {
599 ref().push_back(value);
600 }
601
603 void erase(size_t index)
604 {
605 checkIndex(index);
606 ref().erase(ref().begin() + index);
607 }
608
613 template <typename U = T>
614 std::enable_if_t<!std::is_base_of_v<Base, U>, std::vector<U>>
616 { return std::vector<U>(begin(), end()); }
617
619 [[nodiscard]] auto begin() { return iterator(this, 0); }
620
622 [[nodiscard]] auto end() { return iterator(this, size()); }
623
625 [[nodiscard]] auto begin() const { return const_iterator(this, 0); }
626
628 [[nodiscard]] auto end() const { return const_iterator(this, size()); }
629
630protected:
633 void checkIndex(size_t index) const
634 {
635 assert(index < ref().size());
636 if (index >= ref().size()) {
637 throw std::out_of_range("Index out of range");
638 }
639 }
640
641private:
642 template <typename U, typename... Args>
643 U appendImpl(Args&&... args)
644 {
645 static_assert(std::is_base_of_v<Base, U>, "Array::appendImpl requires a Base-derived element type.");
646 if constexpr (std::is_base_of_v<Object, U>) {
647 ref().push_back(json::object());
648 } else {
649 ref().push_back(json::array());
650 }
651 return U(*this, std::to_string(ref().size() - 1), std::forward<Args>(args)...);
652 }
653
654 template <typename, auto>
655 friend struct detail::ArrayAppendFromMake;
656 template <typename, typename>
657 friend struct detail::ArrayAppendBase;
658};
659
664{
665public:
666 using Object::Object;
667
669 size_t calcArrayIndex() const
670 {
671 return std::stoul(pointer().back());
672 }
673
678 template <typename ContainerType>
679 ContainerType container() const;
680};
681
682class ContentArray;
685{
686protected:
687 static constexpr std::string_view ContentTypeValueDefault = "event";
688
689public:
690 using ArrayElementObject::ArrayElementObject;
691
693
695 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
696 [[nodiscard]] T get() const
697 {
698 return getTypedObject<T>();
699 }
700
703 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
704 [[nodiscard]] T getTypedObject() const
705 {
706 if (type() != T::ContentTypeValue) {
707 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
708 ", got " + type());
709 }
710 return T(root(), pointer());
711 }
712
713 friend class ContentArray;
714};
715
716template <typename ContainerType>
717inline ContainerType ArrayElementObject::container() const
718{
719 if constexpr (std::is_base_of_v<ContainerType, ContentObject>) {
720 const auto obj = parent<Array<ArrayElementObject>>().template parent<ContentObject>();
721 if constexpr (std::is_same_v<ContainerType, ContentObject>) {
722 return obj;
723 } else {
724 MNX_ASSERT_IF(obj.type() != ContainerType::ContentTypeValue) {
725 throw std::invalid_argument(
726 "container(): requested type does not match underlying content object type");
727 }
728 return obj.template get<ContainerType>();
729 }
730 } else {
731 return parent<Array<ArrayElementObject>>().template parent<ContainerType>();
732 }
733}
734
762class ContentArray : public Array<ContentObject>
763{
764public:
766 using BaseArray::BaseArray; // Inherit constructors
767
769 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
770 [[nodiscard]] T get(size_t index) const
771 {
772 this->checkIndex(index);
773 return operator[](index).get<T>();
774 }
775
777 template <typename T,
778 std::enable_if_t<std::is_base_of_v<ContentObject, T> &&
779 !std::is_same_v<T, sequence::Event> &&
780 !std::is_same_v<T, sequence::Space> &&
781 !std::is_same_v<T, sequence::MultiNoteTremolo> &&
782 !std::is_same_v<T, sequence::Tuplet>, int> = 0>
784 {
785 return appendWithType<T>();
786 }
787
789 template <typename T, typename... Args,
790 std::enable_if_t<std::is_base_of_v<ContentObject, T> && (sizeof...(Args) > 0), int> = 0>
791 T append(const Args&... args)
792 {
793 static_assert(!std::is_same_v<T, T>,
794 "ContentArray::append requires explicit specialization for each content type.");
795 return appendWithType<T>(args...);
796 }
797
798 // Prevent untemplated append() calls; callers must use append<T>(...).
799 ContentObject append(...) = delete;
800
801private:
802 template <typename T, typename... Args>
803 T appendWithType(Args&&... args)
804 {
805 auto result = BaseArray::append<T>(std::forward<Args>(args)...);
806 if constexpr (T::ContentTypeValue != ContentObject::ContentTypeValueDefault) {
807 result.set_type(std::string(T::ContentTypeValue));
808 }
809 return result;
810 }
811
814 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
815 [[nodiscard]] T getTypedObject(size_t index) const
816 {
817 this->checkIndex(index);
818 auto element = (*this)[index];
819 if (element.type() != T::ContentTypeValue) {
820 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
821 ", got " + element.type());
822 }
823 return T(root(), pointer() / std::to_string(index));
824 }
825};
826
831template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
833{
834 static const std::unordered_map<std::string, E> stringToEnum();
835
837 static const std::unordered_map<E, std::string> enumToString()
838 {
839 static const std::unordered_map<E, std::string> reverseMap = []() {
840 std::unordered_map<E, std::string> result;
841 for (const auto& element : EnumStringMapping<E>::stringToEnum()) {
842 result.emplace(element.second, element.first);
843 }
844 return result;
845 }();
846 return reverseMap;
847 }
848};
849
853template <typename T>
854class Dictionary : public Object,
855 public detail::DictionaryAppendOverloads<Dictionary<T>, T>
856{
857 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
858 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX dictionary element type.");
859
860private:
861 template <typename DictionaryType, typename IteratorType>
862 struct iter
863 {
864 using value_type = std::pair<const std::string, T>;
865 using difference_type = std::ptrdiff_t;
866 using iterator_category = std::forward_iterator_tag;
867 using pointer = value_type*;
868 using reference = value_type&;
869
870 private:
871 DictionaryType* m_ptr {};
872 IteratorType m_it {};
873 mutable std::optional<value_type> m_pair; // cached key/value
874
875 void update_pair() const
876 {
877 m_pair.reset();
878 if (m_it != m_ptr->ref().end()) {
879 m_pair.emplace(m_it.key(), m_ptr->valueForKey(m_it.key()));
880 }
881 }
882
883 public:
884 iter(DictionaryType* ptr, IteratorType it)
885 : m_ptr(ptr), m_it(it)
886 {
887 update_pair();
888 }
889
890 [[nodiscard]] reference operator*() const { return *m_pair; }
891 [[nodiscard]] pointer operator->() const { return &*m_pair; }
892
893 iter& operator++() { ++m_it; update_pair(); return *this; }
894 iter operator++(int) { iter tmp(*this); ++(*this); return tmp; }
895
896 [[nodiscard]] bool operator!=(const iter& o) const { return m_it != o.m_it; }
897 [[nodiscard]] bool operator==(const iter& o) const { return m_it == o.m_it; }
898 };
899
900public:
902 using value_type = T;
903
904 using iterator = iter<Dictionary, json::iterator>;
905 using const_iterator = iter<const Dictionary, json::const_iterator>;
906
910 Dictionary(const std::shared_ptr<json>& root, json_pointer pointer)
912 {
913 }
914
918 Dictionary(Base& parent, std::string_view key)
919 : Object(parent, key) {}
920
922 [[nodiscard]] size_t size() const { return ref().size(); }
923
925 [[nodiscard]] bool empty() const { return ref().empty(); }
926
928 void clear() { ref().clear(); }
929
933 [[nodiscard]] T at(std::string_view key) const
934 { return valueForKey(key); }
935
937 template <typename U = T>
938 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
939 emplace(std::string_view key, const U& value)
940 {
941 ref()[key] = value;
942 }
943
945 void erase(std::string_view key)
946 {
947 ref().erase(key);
948 }
949
953 [[nodiscard]] auto find(std::string_view key)
954 {
955 auto it = ref().find(key);
956 return (it != ref().end()) ? iterator(this, it) : end();
957 }
958
962 [[nodiscard]] auto find(std::string_view key) const
963 {
964 auto it = ref().find(key);
965 return (it != ref().end()) ? const_iterator(this, it) : end();
966 }
967
970 [[nodiscard]] bool contains(std::string_view key) const
971 { return find(key) != end(); }
972
974 [[nodiscard]] auto begin() { return iterator(this, ref().begin()); }
975
977 [[nodiscard]] auto end() { return iterator(this, ref().end()); }
978
980 [[nodiscard]] auto begin() const { return const_iterator(this, ref().begin()); }
981
983 [[nodiscard]] auto end() const { return const_iterator(this, ref().end()); }
984
985private:
987 [[nodiscard]] T valueForKey(std::string_view key) const
988 {
989 if constexpr (std::is_base_of_v<Base, T>) {
990 return getChild<T>(key);
991 } else {
992 return getChild<SimpleType<T>>(key);
993 }
994 }
995
996 template <typename U, typename... Args>
997 U appendImpl(std::string_view key, Args&&... args)
998 {
999 static_assert(std::is_base_of_v<Base, U>, "Dictionary::appendImpl requires a Base-derived element type.");
1000 if constexpr (std::is_base_of_v<Object, U>) {
1001 ref()[key] = json::object();
1002 } else {
1003 ref()[key] = json::array();
1004 }
1005 return U(*this, key, std::forward<Args>(args)...);
1006 }
1007
1008 template <typename, auto>
1009 friend struct detail::DictionaryAppendFromMake;
1010 template <typename, typename>
1011 friend struct detail::DictionaryAppendBase;
1012};
1013
1014} // namespace mnx
1015
1016#ifndef DOXYGEN_SHOULD_IGNORE_THIS
1017
1018namespace nlohmann {
1019
1020#if defined(_WIN32)
1021// This general adl_serializer is enabled only for enum types.
1022// For some reason MSC does not like the direct function definitions below.
1023template<typename EnumType>
1024struct adl_serializer<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
1025{
1026 template<typename BasicJsonType>
1027 static EnumType from_json(const BasicJsonType& j)
1028 {
1029 // Lookup the string in the specialized map.
1031 auto it = map.find(j.get<std::string>());
1032 if (it != map.end()) {
1033 return it->second;
1034 }
1036 return EnumType{};
1037 }
1038
1039 template<typename BasicJsonType>
1040 static void to_json(BasicJsonType& j, const EnumType& value)
1041 {
1043 auto it = map.find(value);
1044 if (it == map.end()) {
1046 j = BasicJsonType();
1047 return;
1048 }
1049 j = it->second;
1050 }
1051};
1052#else
1053// Clang works with the adl_specialization above, but GCC does not.
1054namespace detail {
1055
1056template<typename BasicJsonType, typename EnumType,
1057 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1058inline void from_json(const BasicJsonType& j, EnumType& value)
1059{
1060 // Lookup the string in the specialized map.
1062 auto it = map.find(j.template get<std::string>());
1063 if (it != map.end()) {
1064 value = it->second;
1065 } else {
1067 value = EnumType{};
1068 }
1069}
1070
1071template<typename BasicJsonType, typename EnumType,
1072 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1073inline void to_json(BasicJsonType& j, EnumType value) noexcept
1074{
1076 auto it = map.find(value);
1077 if (it != map.end()) {
1078 j = it->second;
1079 } else {
1081 j = BasicJsonType();
1082 }
1083}
1084
1085} // namespace detail
1086#endif // defined(_WIN32)
1087
1088} // namespace nlohmann
1089
1090#endif // DOXYGEN_SHOULD_IGNORE_THIS
Represents an MNX object that is included as an array element.
Definition BaseTypes.h:664
ContainerType container() const
Returns the object that owns the content array this element belongs to wrapped as the specified templ...
Definition BaseTypes.h:717
size_t calcArrayIndex() const
Calculates the array index of the current instance within the array.
Definition BaseTypes.h:669
Represents an MNX array, encapsulating property access.
Definition BaseTypes.h:499
T front() const
Access the first element.
Definition BaseTypes.h:565
void checkIndex(size_t index) const
validates that an index is not out of range
Definition BaseTypes.h:633
auto begin()
Returns an iterator to the beginning of the array.
Definition BaseTypes.h:619
size_t size() const
Get the size of the array.
Definition BaseTypes.h:548
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:551
iter< Array > iterator
non-const iterator type
Definition BaseTypes.h:528
iter< const Array > const_iterator
const iterator type
Definition BaseTypes.h:529
T value_type
The type for elements in this Array.
Definition BaseTypes.h:526
auto operator[](size_t index) const
const operator[]
Definition BaseTypes.h:573
Array(Base &parent, std::string_view key)
Creates a new Array class as a child of a JSON node.
Definition BaseTypes.h:544
auto begin() const
Returns a const iterator to the beginning of the array.
Definition BaseTypes.h:625
void erase(size_t index)
Remove an element at a given index.
Definition BaseTypes.h:603
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:615
auto end()
Returns an iterator to the end of the array.
Definition BaseTypes.h:622
T back() const
Access the last element.
Definition BaseTypes.h:570
void clear()
Clear all elements.
Definition BaseTypes.h:554
Array(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Array class around an existing JSON array node.
Definition BaseTypes.h:534
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:597
T at(size_t index) const
Direct getter for a particular element.
Definition BaseTypes.h:557
auto end() const
Returns a const iterator to the end of the array.
Definition BaseTypes.h:628
auto operator[](size_t index)
non-const operator[]
Definition BaseTypes.h:584
Base class wrapper for all MNX JSON nodes.
Definition BaseTypes.h:198
json & ref() const
Convert this node for retrieval.
Definition BaseTypes.h:271
Base(json &&jsonRef, Base &parent, std::string_view key)
Construct a Base reference as a child inside a parent node.
Definition BaseTypes.h:296
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:287
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:310
json & ref()
Access the JSON node for modification.
Definition BaseTypes.h:277
std::optional< T > getOptionalChild(std::string_view key) const
Retrieves an optional child node.
Definition BaseTypes.h:347
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:330
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:280
Class for content arrays.
Definition BaseTypes.h:763
T get(size_t index) const
Retrieve an element from the array as a specific type.
Definition BaseTypes.h:770
T append()
Append an element of the specified type (default overload for no-arg content types).
Definition BaseTypes.h:783
T append(const Args &... args)
Append overload entry point for explicitly specialized argful content types.
Definition BaseTypes.h:791
Base class for objects that are elements of content arrays.
Definition BaseTypes.h:685
MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(std::string, type, std::string(ContentTypeValueDefault))
determines our type in the JSON
static constexpr std::string_view ContentTypeValueDefault
default type value that identifies the type within the content array
Definition BaseTypes.h:687
T get() const
Retrieve an element as a specific type.
Definition BaseTypes.h:696
T getTypedObject() const
Constructs an object of type T if its type matches the JSON type.
Definition BaseTypes.h:704
Represents an MNX dictionary, where each key is a user-defined string.
Definition BaseTypes.h:856
iter< Dictionary, json::iterator > iterator
non-const iterator type
Definition BaseTypes.h:904
size_t size() const
Get the size of the array.
Definition BaseTypes.h:922
auto end() const
Returns a const iterator to the end of the dictionary.
Definition BaseTypes.h:983
auto begin() const
Returns a const iterator to the beginning of the dictionary.
Definition BaseTypes.h:980
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:939
auto find(std::string_view key)
Finds an element by key and returns an iterator.
Definition BaseTypes.h:953
Dictionary(Base &parent, std::string_view key)
Creates a new Dictionary class as a child of a JSON node.
Definition BaseTypes.h:918
iter< const Dictionary, json::const_iterator > const_iterator
const iterator type
Definition BaseTypes.h:905
T value_type
The type for elements in this Array.
Definition BaseTypes.h:902
auto begin()
Returns an iterator to the beginning of the dictionary.
Definition BaseTypes.h:974
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:925
auto find(std::string_view key) const
Finds an element by key and returns a const iterator.
Definition BaseTypes.h:962
void erase(std::string_view key)
Remove an element at a given key.
Definition BaseTypes.h:945
Dictionary(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Dictionary class around an existing JSON node.
Definition BaseTypes.h:910
T at(std::string_view key) const
Direct getter for a particular element.
Definition BaseTypes.h:933
auto end()
Returns an iterator to the end of the dictionary.
Definition BaseTypes.h:977
bool contains(std::string_view key) const
Returns true if the key exists in in the dictionary.
Definition BaseTypes.h:970
void clear()
Clear all elements.
Definition BaseTypes.h:928
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:412
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:417
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:435
std::optional< json > getExtension(const std::string &key) const
Gets a vendor extension value from _x.
Definition BaseTypes.h:442
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:427
Allows access to a fundamental type (number, boolean, string) in a JSON node.
Definition BaseTypes.h:458
SimpleType(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps a SimpleType class around an existing JSON object node.
Definition BaseTypes.h:467
bool operator==(const T &src) const
Equality comparison with value type.
Definition BaseTypes.h:486
T value_type
value type of this SimpleType
Definition BaseTypes.h:462
SimpleType & operator=(const T &src)
Allow assignment to underlying json reference.
Definition BaseTypes.h:479
Represents a musical event within a sequence.
Definition Sequence.h:418
Represents a multi-note tremolo sequence within a sequence.
Definition Sequence.h:564
Occupies metric space without showing anything.
Definition Sequence.h:492
Represents a tuplet sequence within a sequence.
Definition Sequence.h:615
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:406
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:833
static const std::unordered_map< E, std::string > enumToString()
maps enum values to strings
Definition BaseTypes.h:837
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