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 ContentObject;
74template <typename T> class Array;
75template <typename T> class Dictionary;
76
77namespace sequence {
78class Event;
79class Space;
82class Tuplet;
83} // namespace sequence
84
85namespace layout {
87}
88
89namespace text {
91}
92
93namespace part {
95}
96
97namespace validation {
98class SemanticValidator;
99}; // namespace validation
100
101#ifndef DOXYGEN_SHOULD_IGNOR_THIS
102
103namespace scope {
104struct Default {};
107} // namespace scope
108
109#endif
110
111namespace detail {
112
114template <typename T, auto MakeFunc>
116
118template <typename T, typename R, typename... Args, R(*MakeFunc)(Args...)>
119struct ArrayAppendFromMake<T, MakeFunc>
120{
125 T append(Args... args)
126 {
127 return static_cast<Array<T>&>(*this).template appendImpl<T>(std::forward<Args>(args)...);
128 }
129};
130
132template <typename Derived, typename T>
134{
139 template <typename U = T, typename... Args,
140 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
141 U append(Args&&... args)
142 {
143 return static_cast<Derived&>(*this).template appendImpl<U>(std::forward<Args>(args)...);
144 }
145};
146
147template <typename Derived, typename T, typename = void>
148struct ArrayAppendOverloads : ArrayAppendBase<Derived, T> {};
149
150template <typename Derived, typename T>
151struct ArrayAppendOverloads<Derived, T, std::void_t<decltype(&T::make)>>
152 : ArrayAppendBase<Derived, T>,
153 ArrayAppendFromMake<T, &T::make>
154{
155 using ArrayAppendBase<Derived, T>::append;
156 using ArrayAppendFromMake<T, &T::make>::append;
157};
158
160template <typename T, auto MakeFunc>
162
164template <typename T, typename R, typename... Args, R(*MakeFunc)(Args...)>
165struct DictionaryAppendFromMake<T, MakeFunc>
166{
171 T append(std::string_view key, Args... args)
172 {
173 return static_cast<Dictionary<T>&>(*this).template appendImpl<T>(key, std::forward<Args>(args)...);
174 }
175};
176
178template <typename Derived, typename T>
180{
185 template <typename U = T, typename... Args,
186 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
187 U append(std::string_view key, Args&&... args)
188 {
189 return static_cast<Derived&>(*this).template appendImpl<U>(key, std::forward<Args>(args)...);
190 }
191};
192
193template <typename Derived, typename T, typename = void>
195
196template <typename Derived, typename T>
197struct DictionaryAppendOverloads<Derived, T, std::void_t<decltype(&T::make)>>
198 : DictionaryAppendBase<Derived, T>,
199 DictionaryAppendFromMake<T, &T::make>
200{
201 using DictionaryAppendBase<Derived, T>::append;
202 using DictionaryAppendFromMake<T, &T::make>::append;
203};
204
205} // namespace detail
206
207
211class Base
212{
213public:
214 virtual ~Base() = default;
215
217 Base(const Base& src) : m_root(src.m_root), m_pointer(src.m_pointer)
218 {}
219
221 Base(Base&& src) noexcept : m_root(src.m_root), // m_root must be copied (not moved)
222 m_pointer(std::move(src.m_pointer))
223 {}
224
226 Base& operator=(const Base& src)
227 {
228 if (this != &src) {
229 if (m_root != src.m_root) {
230 throw std::logic_error("Assignment from a different JSON document is not allowed.");
231 }
232 m_pointer = src.m_pointer;
233 }
234 return *this;
235 }
236
239 {
240 if (this != &src) {
241 if (m_root != src.m_root) {
242 throw std::logic_error("Assignment from a different JSON document is not allowed.");
243 }
244 m_pointer = std::move(src.m_pointer);
245 }
246 return *this;
247 }
248
251 [[nodiscard]] std::string dump(int indents = -1) const
252 {
253 return ref().dump(indents);
254 }
255
259 template <typename T>
260 [[nodiscard]] T parent() const
261 {
262 static_assert(std::is_base_of_v<Base, T>, "Template type mush be derived from Base.");
263 return T(m_root, m_pointer.parent_pointer());
264 }
265
270 template <typename T, typename Scope = scope::Default>
271 [[nodiscard]] std::optional<T> getEnclosingElement() const;
272
274 [[nodiscard]] json_pointer pointer() const { return m_pointer; }
275
277 [[nodiscard]] Document document() const;
278
280 [[nodiscard]] bool empty() const { return ref().empty(); }
281
282protected:
288 [[nodiscard]] json& ref() const { return resolve_pointer(); }
289
294 [[nodiscard]] json& ref() { return resolve_pointer(); }
295
297 [[nodiscard]] const std::shared_ptr<json>& root() const { return m_root; }
298
304 Base(const std::shared_ptr<json>& root, json_pointer pointer)
305 : m_root(root), m_pointer(std::move(pointer)) {}
306
313 Base(json&& jsonRef, Base& parent, std::string_view key)
314 : m_root(parent.m_root), m_pointer(parent.m_pointer / std::string(key))
315 {
316 (*m_root)[m_pointer] = std::move(jsonRef);
317 }
318
326 template <typename T>
327 [[nodiscard]] T getChild(std::string_view key) const
328 {
329 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
330
331 json_pointer childPointer = m_pointer / std::string(key);
332 if (!checkKeyIsValid<T>(childPointer)) {
333 throw std::out_of_range("Child node not found: " + std::string(key));
334 }
335
336 return T(m_root, childPointer);
337 }
338
346 template <typename T>
347 T setChild(std::string_view key, const T& value)
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 (*m_root)[childPointer] = value.ref();
353 return T(m_root, childPointer);
354 }
355
363 template <typename T>
364 [[nodiscard]] std::optional<T> getOptionalChild(std::string_view key) const
365 {
366 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
367
368 json_pointer childPointer = m_pointer / std::string(key);
369 if (!checkKeyIsValid<T>(childPointer)) {
370 return std::nullopt;
371 }
372
373 return T(m_root, childPointer);
374 }
375
376private:
384 template <typename T>
385 [[nodiscard]] bool checkKeyIsValid(const json_pointer& pointer) const
386 {
387 if (!(*m_root).contains(pointer)) {
388 return false;
389 }
390
391 const json& node = (*m_root).at(pointer);
392
393 if constexpr (std::is_base_of_v<Object, T>) {
394 if (!node.is_object()) {
395 throw std::runtime_error("Expected an object for: " + pointer.to_string());
396 }
397 } else if constexpr (std::is_base_of_v<Array<typename T::value_type>, T>) {
398 if (!node.is_array()) {
399 throw std::runtime_error("Expected an array for: " + pointer.to_string());
400 }
401 }
402
403 return true;
404 }
405
411 [[nodiscard]] json& resolve_pointer() const
412 {
413 return (*m_root).at(m_pointer); // Throws if invalid
414 }
415
416 const std::shared_ptr<json> m_root;
417 json_pointer m_pointer;
418
419 friend class validation::SemanticValidator;
420};
421
423using ErrorHandler = std::function<void(const std::string& message, const Base& location)>;
424
428class Object : public Base
429{
430public:
432 Object() : Object(std::make_shared<json>(json::object()), json_pointer{})
433 {}
434
438 Object(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
439 {
440 if (!ref().is_object()) {
441 throw std::invalid_argument("mnx::Object must wrap a JSON object.");
442 }
443 }
444
448 Object(Base& parent, std::string_view key)
449 : Base(json::object(), parent, key) {}
450
451 MNX_OPTIONAL_PROPERTY(std::string, _c);
453 MNX_OPTIONAL_PROPERTY(std::string, id);
454
456 void setExtension(const std::string_view key, const json& value)
457 {
458 auto extensions = ensure__x();
459 extensions.ref()[key] = value;
460 }
461
463 [[nodiscard]] std::optional<json> getExtension(const std::string_view key) const
464 {
465 if (auto extensions = _x()) {
466 const auto it = extensions->ref().find(key);
467 if (it != extensions->ref().end()) {
468 return *it;
469 }
470 }
471 return std::nullopt;
472 }
473
475 void clearExtension(const std::string_view key)
476 {
477 if (auto extensions = _x()) {
478 extensions->ref().erase(key);
479 if (extensions->empty()) {
480 clear__x();
481 }
482 }
483 }
484};
485
488template <typename T, std::enable_if_t<!std::is_base_of_v<Base, T>, int> = 0>
489class SimpleType : public Base
490{
491 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>, "This template is for simple JSON classes");
492
493public:
494 using value_type = T;
495
499 SimpleType(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
500 {
501 }
502
504 operator T() const
505 {
506 return ref().template get<T>();
507 }
508
511 SimpleType& operator=(const T& src)
512 {
513 ref() = src;
514 return *this;
515 }
516
518 [[nodiscard]] bool operator==(const T& src) const
519 {
520 return src == ref().template get<T>();
521 }
522};
523
524class ArrayElementObject;
528template <typename T>
529class Array : public Base,
530 public detail::ArrayAppendOverloads<Array<T>, T>
531{
532 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
533 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX array element type.");
534
535private:
536 template<typename ArrayType>
537 struct iter
538 {
539 private:
540 ArrayType* m_ptr;
541 mutable size_t m_idx;
542
543 public:
544 using iterator_category = std::input_iterator_tag;
545 using value_type = T;
546 using difference_type = std::ptrdiff_t;
547 using pointer = void; // not meaningful for proxy/value-returning iterators
548 using reference = T; // since operator* returns by value
549
550 iter(ArrayType* ptr, size_t idx) : m_ptr(ptr), m_idx(idx) {}
551 T operator*() const { return (*m_ptr)[m_idx]; }
552 iter& operator++() { ++m_idx; return *this; }
553 bool operator!=(const iter& o) const { return m_idx != o.m_idx; }
554 };
555
556public:
558 using value_type = T;
559
560 using iterator = iter<Array>;
561 using const_iterator = iter<const Array>;
562
564 Array() : Array(std::make_shared<json>(json::array()), json_pointer{})
565 {}
566
570 Array(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
571 {
572 if (!ref().is_array()) {
573 throw std::invalid_argument("mnx::Array must wrap a JSON array.");
574 }
575 }
576
580 Array(Base& parent, std::string_view key)
581 : Base(json::array(), parent, key) {}
582
584 [[nodiscard]] size_t size() const { return ref().size(); }
585
587 [[nodiscard]] bool empty() const { return ref().empty(); }
588
590 void clear() { ref().clear(); }
591
593 [[nodiscard]] T at(size_t index) const
594 {
595 return operator[](index);
596 }
597
601 [[nodiscard]] T front() const { return (*this)[0]; }
602
606 [[nodiscard]] T back() const { return (*this)[size() - 1]; }
607
609 [[nodiscard]] auto operator[](size_t index) const
610 {
611 checkIndex(index);
612 if constexpr (std::is_base_of_v<Base, T>) {
613 return getChild<T>(std::to_string(index));
614 } else {
615 return getChild<SimpleType<T>>(std::to_string(index));
616 }
617 }
618
620 [[nodiscard]] auto operator[](size_t index)
621 {
622 checkIndex(index);
623 if constexpr (std::is_base_of_v<Base, T>) {
624 return getChild<T>(std::to_string(index));
625 } else {
626 return getChild<SimpleType<T>>(std::to_string(index));
627 }
628 }
629
631 template <typename U = T>
632 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
633 push_back(const U& value)
634 {
635 ref().push_back(value);
636 }
637
639 template <typename InputIt, typename U = T>
640 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
641 assign(InputIt first, InputIt last)
642 {
643 ref() = json::array();
644 for (; first != last; ++first) {
645 ref().push_back(*first);
646 }
647 }
648
650 template <typename U = T>
651 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
652 assign(const std::vector<U>& values)
653 {
654 assign(values.begin(), values.end());
655 }
656
658 void erase(size_t index)
659 {
660 checkIndex(index);
661 ref().erase(ref().begin() + index);
662 }
663
668 template <typename U = T>
669 std::enable_if_t<!std::is_base_of_v<Base, U>, std::vector<U>>
671 { return std::vector<U>(begin(), end()); }
672
674 [[nodiscard]] auto begin() { return iterator(this, 0); }
675
677 [[nodiscard]] auto end() { return iterator(this, size()); }
678
680 [[nodiscard]] auto begin() const { return const_iterator(this, 0); }
681
683 [[nodiscard]] auto end() const { return const_iterator(this, size()); }
684
685protected:
688 void checkIndex(size_t index) const
689 {
690 assert(index < ref().size());
691 if (index >= ref().size()) {
692 throw std::out_of_range("Index out of range");
693 }
694 }
695
696private:
697 template <typename U, typename... Args>
698 U appendImpl(Args&&... args)
699 {
700 static_assert(std::is_base_of_v<Base, U>, "Array::appendImpl requires a Base-derived element type.");
701 if constexpr (std::is_base_of_v<Object, U>) {
702 ref().push_back(json::object());
703 } else {
704 ref().push_back(json::array());
705 }
706 return U(*this, std::to_string(ref().size() - 1), std::forward<Args>(args)...);
707 }
708
709 template <typename, auto>
710 friend struct detail::ArrayAppendFromMake;
711 template <typename, typename>
712 friend struct detail::ArrayAppendBase;
713};
714
719{
720public:
721 using Object::Object;
722
724 size_t calcArrayIndex() const
725 {
726 return std::stoul(pointer().back());
727 }
728
733 template <typename ContainerType>
734 ContainerType container() const;
735};
736
737template <typename BaseContentT>
738class ContentArray;
740template <typename BaseContentT>
742{
743public:
744 virtual std::string_view defaultType() const { return ""; };
745 using ArrayElementObject::ArrayElementObject;
746
747 MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(std::string, type, std::string(defaultType()));
748
751 template <typename T, std::enable_if_t<std::is_base_of_v<BaseContentT, T>, int> = 0>
752 [[nodiscard]] T get() const
753 {
754 if (type() != T::ContentTypeValue) {
755 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
756 ", got " + type());
757 }
758 return T(root(), pointer());
759 }
760
761 friend class ContentArray<BaseContentT>;
762};
763
764template <typename ContainerType>
765inline ContainerType ArrayElementObject::container() const
766{
767 if constexpr (std::is_base_of_v<ContentObject<ContainerType>, ContainerType>) {
768 const auto obj = parent<Array<ArrayElementObject>>().template parent<ContainerType>();
769 if constexpr (std::is_same_v<ContainerType, ContentObject<ContainerType>>
770 || std::is_same_v<ContainerType, sequence::SequenceContentObject>
771 || std::is_same_v<ContainerType, layout::LayoutContentObject>
772 || std::is_same_v<ContainerType, text::TextContentObject>
773 || std::is_same_v<ContainerType, part::DynamicGroupBase>) {
774 return obj;
775 } else {
776 MNX_ASSERT_IF(obj.type() != ContainerType::ContentTypeValue) {
777 throw std::invalid_argument(
778 "container(): requested type does not match underlying content object type");
779 }
780 return obj.template get<ContainerType>();
781 }
782 } else {
783 return parent<Array<ArrayElementObject>>().template parent<ContainerType>();
784 }
785}
786
791template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
793{
794 static const std::unordered_map<std::string, E> stringToEnum();
795
797 static const std::unordered_map<E, std::string> enumToString()
798 {
799 static const std::unordered_map<E, std::string> reverseMap = []() {
800 std::unordered_map<E, std::string> result;
801 for (const auto& element : EnumStringMapping<E>::stringToEnum()) {
802 result.emplace(element.second, element.first);
803 }
804 return result;
805 }();
806 return reverseMap;
807 }
808};
809
813template <typename T>
814class Dictionary : public Object,
815 public detail::DictionaryAppendOverloads<Dictionary<T>, T>
816{
817 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
818 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX dictionary element type.");
819
820private:
821 template <typename DictionaryType, typename IteratorType>
822 struct iter
823 {
824 using value_type = std::pair<const std::string, T>;
825 using difference_type = std::ptrdiff_t;
826 using iterator_category = std::forward_iterator_tag;
827 using pointer = value_type*;
828 using reference = value_type&;
829
830 private:
831 DictionaryType* m_ptr {};
832 IteratorType m_it {};
833 mutable std::optional<value_type> m_pair; // cached key/value
834
835 void update_pair() const
836 {
837 m_pair.reset();
838 if (m_it != m_ptr->ref().end()) {
839 m_pair.emplace(m_it.key(), m_ptr->valueForKey(m_it.key()));
840 }
841 }
842
843 public:
844 iter(DictionaryType* ptr, IteratorType it)
845 : m_ptr(ptr), m_it(it)
846 {
847 update_pair();
848 }
849
850 [[nodiscard]] reference operator*() const { return *m_pair; }
851 [[nodiscard]] pointer operator->() const { return &*m_pair; }
852
853 iter& operator++() { ++m_it; update_pair(); return *this; }
854 iter operator++(int) { iter tmp(*this); ++(*this); return tmp; }
855
856 [[nodiscard]] bool operator!=(const iter& o) const { return m_it != o.m_it; }
857 [[nodiscard]] bool operator==(const iter& o) const { return m_it == o.m_it; }
858 };
859
860public:
862 using value_type = T;
863
864 using iterator = iter<Dictionary, json::iterator>;
865 using const_iterator = iter<const Dictionary, json::const_iterator>;
866
870 Dictionary(const std::shared_ptr<json>& root, json_pointer pointer)
872 {
873 }
874
878 Dictionary(Base& parent, std::string_view key)
879 : Object(parent, key) {}
880
882 [[nodiscard]] size_t size() const { return ref().size(); }
883
885 [[nodiscard]] bool empty() const { return ref().empty(); }
886
888 void clear() { ref().clear(); }
889
893 [[nodiscard]] T at(std::string_view key) const
894 { return valueForKey(key); }
895
897 template <typename U = T>
898 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
899 emplace(std::string_view key, const U& value)
900 {
901 ref()[key] = value;
902 }
903
905 void erase(std::string_view key)
906 {
907 ref().erase(key);
908 }
909
913 [[nodiscard]] auto find(std::string_view key)
914 {
915 auto it = ref().find(key);
916 return (it != ref().end()) ? iterator(this, it) : end();
917 }
918
922 [[nodiscard]] auto find(std::string_view key) const
923 {
924 auto it = ref().find(key);
925 return (it != ref().end()) ? const_iterator(this, it) : end();
926 }
927
930 [[nodiscard]] bool contains(std::string_view key) const
931 { return find(key) != end(); }
932
934 [[nodiscard]] auto begin() { return iterator(this, ref().begin()); }
935
937 [[nodiscard]] auto end() { return iterator(this, ref().end()); }
938
940 [[nodiscard]] auto begin() const { return const_iterator(this, ref().begin()); }
941
943 [[nodiscard]] auto end() const { return const_iterator(this, ref().end()); }
944
945private:
947 [[nodiscard]] T valueForKey(std::string_view key) const
948 {
949 if constexpr (std::is_base_of_v<Base, T>) {
950 return getChild<T>(key);
951 } else {
952 return getChild<SimpleType<T>>(key);
953 }
954 }
955
956 template <typename U, typename... Args>
957 U appendImpl(std::string_view key, Args&&... args)
958 {
959 static_assert(std::is_base_of_v<Base, U>, "Dictionary::appendImpl requires a Base-derived element type.");
960 if constexpr (std::is_base_of_v<Object, U>) {
961 ref()[key] = json::object();
962 } else {
963 ref()[key] = json::array();
964 }
965 return U(*this, key, std::forward<Args>(args)...);
966 }
967
968 template <typename, auto>
969 friend struct detail::DictionaryAppendFromMake;
970 template <typename, typename>
971 friend struct detail::DictionaryAppendBase;
972};
973
974} // namespace mnx
975
976#ifndef DOXYGEN_SHOULD_IGNORE_THIS
977
978namespace nlohmann {
979
980#if defined(_WIN32)
981// This general adl_serializer is enabled only for enum types.
982// For some reason MSC does not like the direct function definitions below.
983template<typename EnumType>
984struct adl_serializer<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
985{
986 template<typename BasicJsonType>
987 static EnumType from_json(const BasicJsonType& j)
988 {
989 // Lookup the string in the specialized map.
991 auto it = map.find(j.template get<std::string>());
992 if (it != map.end()) {
993 return it->second;
994 }
996 return EnumType{};
997 }
998
999 template<typename BasicJsonType>
1000 static void to_json(BasicJsonType& j, const EnumType& value)
1001 {
1003 auto it = map.find(value);
1004 if (it == map.end()) {
1006 j = BasicJsonType();
1007 return;
1008 }
1009 j = it->second;
1010 }
1011};
1012#else
1013// Clang works with the adl_specialization above, but GCC does not.
1014namespace detail {
1015
1016template<typename BasicJsonType, typename EnumType,
1017 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1018inline void from_json(const BasicJsonType& j, EnumType& value)
1019{
1020 // Lookup the string in the specialized map.
1022 auto it = map.find(j.template get<std::string>());
1023 if (it != map.end()) {
1024 value = it->second;
1025 } else {
1027 value = EnumType{};
1028 }
1029}
1030
1031template<typename BasicJsonType, typename EnumType,
1032 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1033inline void to_json(BasicJsonType& j, EnumType value) noexcept
1034{
1036 auto it = map.find(value);
1037 if (it != map.end()) {
1038 j = it->second;
1039 } else {
1041 j = BasicJsonType();
1042 }
1043}
1044
1045} // namespace detail
1046#endif // defined(_WIN32)
1047
1048} // namespace nlohmann
1049
1050#endif // DOXYGEN_SHOULD_IGNORE_THIS
Represents an MNX object that is included as an array element.
Definition BaseTypes.h:719
ContainerType container() const
Returns the object that owns the content array this element belongs to wrapped as the specified templ...
Definition BaseTypes.h:765
size_t calcArrayIndex() const
Calculates the array index of the current instance within the array.
Definition BaseTypes.h:724
Represents an MNX array, encapsulating property access.
Definition BaseTypes.h:531
std::enable_if_t<!std::is_base_of_v< Base, U >, void > assign(const std::vector< U > &values)
Replace all elements with values from a vector. (Available only for primitive types)
Definition BaseTypes.h:652
T front() const
Access the first element.
Definition BaseTypes.h:601
void checkIndex(size_t index) const
validates that an index is not out of range
Definition BaseTypes.h:688
std::enable_if_t<!std::is_base_of_v< Base, U >, void > assign(InputIt first, InputIt last)
Replace all elements with values from an iterator range. (Available only for primitive types)
Definition BaseTypes.h:641
auto begin()
Returns an iterator to the beginning of the array.
Definition BaseTypes.h:674
size_t size() const
Get the size of the array.
Definition BaseTypes.h:584
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:587
iter< Array > iterator
non-const iterator type
Definition BaseTypes.h:560
iter< const Array > const_iterator
const iterator type
Definition BaseTypes.h:561
T value_type
The type for elements in this Array.
Definition BaseTypes.h:558
auto operator[](size_t index) const
const operator[]
Definition BaseTypes.h:609
Array(Base &parent, std::string_view key)
Creates a new Array class as a child of a JSON node.
Definition BaseTypes.h:580
auto begin() const
Returns a const iterator to the beginning of the array.
Definition BaseTypes.h:680
void erase(size_t index)
Remove an element at a given index.
Definition BaseTypes.h:658
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:670
auto end()
Returns an iterator to the end of the array.
Definition BaseTypes.h:677
T back() const
Access the last element.
Definition BaseTypes.h:606
Array()
Constructor for detached array instance.
Definition BaseTypes.h:564
void clear()
Clear all elements.
Definition BaseTypes.h:590
Array(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Array class around an existing JSON array node.
Definition BaseTypes.h:570
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:633
T at(size_t index) const
Direct getter for a particular element.
Definition BaseTypes.h:593
auto end() const
Returns a const iterator to the end of the array.
Definition BaseTypes.h:683
auto operator[](size_t index)
non-const operator[]
Definition BaseTypes.h:620
Base class wrapper for all MNX JSON nodes.
Definition BaseTypes.h:212
json & ref() const
Convert this node for retrieval.
Definition BaseTypes.h:288
Base(json &&jsonRef, Base &parent, std::string_view key)
Construct a Base reference as a child inside a parent node.
Definition BaseTypes.h:313
json_pointer pointer() const
Returns the json_pointer for this node.
Definition BaseTypes.h:274
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:304
T parent() const
Returns the parent object for this node.
Definition BaseTypes.h:260
T getChild(std::string_view key) const
Retrieves and validates a required child node.
Definition BaseTypes.h:327
json & ref()
Access the JSON node for modification.
Definition BaseTypes.h:294
std::optional< T > getOptionalChild(std::string_view key) const
Retrieves an optional child node.
Definition BaseTypes.h:364
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:70
T setChild(std::string_view key, const T &value)
Sets a child node.
Definition BaseTypes.h:347
bool empty() const
Returns true if the object is empty.
Definition BaseTypes.h:280
Base(const Base &src)
Copy constructor.
Definition BaseTypes.h:217
Base & operator=(const Base &src)
Copy assignment operator.
Definition BaseTypes.h:226
Base(Base &&src) noexcept
Move constructor.
Definition BaseTypes.h:221
Base & operator=(Base &&src)
Move assignment operator.
Definition BaseTypes.h:238
std::string dump(int indents=-1) const
Dumps the branch to a string. Useful in debugging.
Definition BaseTypes.h:251
Document document() const
Returns the document root.
Definition Implementations.cpp:121
const std::shared_ptr< json > & root() const
Returns the root.
Definition BaseTypes.h:297
Class for content arrays.
Definition ContentArray.h:49
Base class for objects that are elements of content arrays.
Definition BaseTypes.h:742
MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(std::string, type, std::string(defaultType()))
determines our type in the JSON
T get() const
Constructs an object of type T if its type matches the JSON type.
Definition BaseTypes.h:752
Represents an MNX dictionary, where each key is a user-defined string.
Definition BaseTypes.h:816
iter< Dictionary, json::iterator > iterator
non-const iterator type
Definition BaseTypes.h:864
size_t size() const
Get the size of the array.
Definition BaseTypes.h:882
auto end() const
Returns a const iterator to the end of the dictionary.
Definition BaseTypes.h:943
auto begin() const
Returns a const iterator to the beginning of the dictionary.
Definition BaseTypes.h:940
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:899
auto find(std::string_view key)
Finds an element by key and returns an iterator.
Definition BaseTypes.h:913
Dictionary(Base &parent, std::string_view key)
Creates a new Dictionary class as a child of a JSON node.
Definition BaseTypes.h:878
iter< const Dictionary, json::const_iterator > const_iterator
const iterator type
Definition BaseTypes.h:865
T value_type
The type for elements in this Array.
Definition BaseTypes.h:862
auto begin()
Returns an iterator to the beginning of the dictionary.
Definition BaseTypes.h:934
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:885
auto find(std::string_view key) const
Finds an element by key and returns a const iterator.
Definition BaseTypes.h:922
void erase(std::string_view key)
Remove an element at a given key.
Definition BaseTypes.h:905
Dictionary(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Dictionary class around an existing JSON node.
Definition BaseTypes.h:870
T at(std::string_view key) const
Direct getter for a particular element.
Definition BaseTypes.h:893
auto end()
Returns an iterator to the end of the dictionary.
Definition BaseTypes.h:937
bool contains(std::string_view key) const
Returns true if the key exists in in the dictionary.
Definition BaseTypes.h:930
void clear()
Clear all elements.
Definition BaseTypes.h:888
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:134
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
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:438
void setExtension(const std::string_view key, const json &value)
Sets a vendor extension value in _x, creating _x when needed.
Definition BaseTypes.h:456
Object()
Constructor fresh document or detached instance.
Definition BaseTypes.h:432
MNX_OPTIONAL_CHILD(Object, _x)
Vendor-defined dictionary.
std::optional< json > getExtension(const std::string_view key) const
Gets a vendor extension value from _x.
Definition BaseTypes.h:463
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:448
Allows access to a fundamental type (number, boolean, string) in a JSON node.
Definition BaseTypes.h:490
SimpleType(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps a SimpleType class around an existing JSON object node.
Definition BaseTypes.h:499
bool operator==(const T &src) const
Equality comparison with value type.
Definition BaseTypes.h:518
T value_type
value type of this SimpleType
Definition BaseTypes.h:494
SimpleType & operator=(const T &src)
Allow assignment to underlying json reference.
Definition BaseTypes.h:511
Definition Layout.h:39
Base class for dynamics.
Definition Dynamics.h:41
Represents a musical event within a sequence.
Definition Sequence.h:445
Represents a multi-note tremolo sequence within a sequence.
Definition Sequence.h:592
Definition Sequence.h:45
Occupies metric space without showing anything.
Definition Sequence.h:520
Represents a tuplet sequence within a sequence.
Definition Sequence.h:643
Base class for formatted-text content objects.
Definition FormattedText.h:50
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:423
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:793
static const std::unordered_map< E, std::string > enumToString()
maps enum values to strings
Definition BaseTypes.h:797
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:134
U append(Args &&... args)
Append a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:141
T append(Args... args)
Append a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:125
Adds an append(...) overload that mirrors a type's static make(...) signature for arrays.
Definition BaseTypes.h:115
Definition BaseTypes.h:148
Base append implementation for dictionaries of Base-derived types.
Definition BaseTypes.h:180
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:187
T append(std::string_view key, Args... args)
Create a new element using the input key. (Available only for Base types)
Definition BaseTypes.h:171
Adds an append(key, ...) overload that mirrors a type's static make(...) signature for dictionaries.
Definition BaseTypes.h:161
Definition BaseTypes.h:194
Definition BaseTypes.h:104
Definition BaseTypes.h:106
Definition BaseTypes.h:105