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 <type_traits>
30
31#include "nlohmann/json.hpp"
32
44#define MNX_REQUIRED_PROPERTY(TYPE, NAME) \
45 TYPE NAME() const { \
46 if (!ref().contains(#NAME)) { \
47 throw std::runtime_error("Missing required property: " #NAME); \
48 } \
49 return ref()[#NAME].get<TYPE>(); \
50 } \
51 void set_##NAME(const TYPE& value) { ref()[#NAME] = value; } \
52 static_assert(true, "") // require semicolon after macro
53
66 #define MNX_ARRAY_ELEMENT_PROPERTY(TYPE, NAME, INDEX) \
67 static_assert(std::is_integral_v<decltype(INDEX)>, "array index must be an integer type"); \
68 TYPE NAME() const { return (*this)[INDEX]; } \
69 void set_##NAME(const TYPE& value) { (*this)[INDEX] = value; } \
70 static_assert(true, "") // require semicolon after macro
71
88 #define MNX_OPTIONAL_NAMED_PROPERTY(TYPE, NAME, KEY) \
89 std::optional<TYPE> NAME() const { \
90 return ref().contains(KEY) ? std::optional<TYPE>(ref()[KEY].get<TYPE>()) : std::nullopt; \
91 } \
92 TYPE NAME##_or(const TYPE& defaultVal) const { \
93 return ref().contains(KEY) ? ref()[KEY].get<TYPE>() : defaultVal; \
94 } \
95 void set_##NAME(const TYPE& value) { ref()[KEY] = value; } \
96 void clear_##NAME() { ref().erase(KEY); } \
97 static_assert(true, "") // require semicolon after macro
98
112 #define MNX_OPTIONAL_PROPERTY(TYPE, NAME) MNX_OPTIONAL_NAMED_PROPERTY(TYPE, NAME, #NAME)
113
129#define MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(TYPE, NAME, DEFAULT) \
130 TYPE NAME() const { \
131 return ref().contains(#NAME) ? ref()[#NAME].get<TYPE>() : DEFAULT; \
132 } \
133 void set_##NAME(const TYPE& value) { ref()[#NAME] = value; } \
134 void clear_##NAME() { ref().erase(#NAME); } \
135 void set_or_clear_##NAME(const TYPE& value) { \
136 if (value == DEFAULT) clear_##NAME(); \
137 else set_##NAME(value); \
138 } \
139 static_assert(true, "") // require semicolon after macro
140
152 #define MNX_REQUIRED_CHILD(TYPE, NAME) \
153 TYPE NAME() const { return getChild<TYPE>(#NAME); } \
154 template<typename... Args> \
155 TYPE create_##NAME(Args&&... args) { \
156 return setChild(#NAME, TYPE(*this, #NAME, std::forward<Args>(args)...)); \
157 } \
158 static_assert(true, "") // require semicolon after macro
159
172 #define MNX_OPTIONAL_CHILD(TYPE, NAME) \
173 std::optional<TYPE> NAME() const { return getOptionalChild<TYPE>(#NAME); } \
174 template<typename... Args> \
175 TYPE create_##NAME(Args&&... args) { \
176 return setChild(#NAME, TYPE(*this, #NAME, std::forward<Args>(args)...)); \
177 } \
178 void clear_##NAME() { ref().erase(#NAME); } \
179 static_assert(true, "") // require semicolon after macro
180
181#define MNX_ASSERT_IF(TEST) \
182assert(!(TEST)); \
183if (TEST)
184
191namespace mnx {
192
194inline constexpr int MNX_VERSION = 1;
195
196using json = nlohmann::ordered_json;
197using json_pointer = json::json_pointer;
198
199class Object;
200template <typename T> class Array;
201
202namespace validation {
203class SemanticValidator;
204}; // namespace validation
205
209class Base
210{
211public:
212 virtual ~Base() = default;
213
215 Base(const Base& src) : m_root(src.m_root), m_pointer(src.m_pointer)
216 {}
217
219 Base(Base&& src) noexcept : m_root(src.m_root), // m_root must be copied (not moved)
220 m_pointer(std::move(src.m_pointer))
221 {}
222
224 Base& operator=(const Base& src)
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 = src.m_pointer;
231 }
232 return *this;
233 }
234
237 {
238 if (this != &src) {
239 if (m_root != src.m_root) {
240 throw std::logic_error("Assignment from a different JSON document is not allowed.");
241 }
242 m_pointer = std::move(src.m_pointer);
243 }
244 return *this;
245 }
246
249 std::string dump(int indents = -1)
250 {
251 return ref().dump(indents);
252 }
253
257 template <typename T>
258 T parent() const
259 {
260 static_assert(std::is_base_of_v<Base, T>, "Template type mush be derived from Base.");
261 return T(m_root, m_pointer.parent_pointer());
262 }
263
267 template <typename T>
268 std::optional<T> getEnclosingElement() const;
269
271 json_pointer pointer() const { return m_pointer; }
272
273protected:
279 json& ref() const { return resolve_pointer(); }
280
285 json& ref() { return resolve_pointer(); }
286
288 const std::shared_ptr<json>& root() const { return m_root; }
289
295 Base(const std::shared_ptr<json>& root, json_pointer pointer)
296 : m_root(root), m_pointer(std::move(pointer)) {}
297
304 Base(json&& jsonRef, Base& parent, const std::string_view& key)
305 : m_root(parent.m_root), m_pointer(parent.m_pointer / std::string(key))
306 {
307 (*m_root)[m_pointer] = std::move(jsonRef);
308 }
309
317 template <typename T>
318 T getChild(const std::string_view& key) const
319 {
320 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
321
322 json_pointer childPointer = m_pointer / std::string(key);
323 if (!checkKeyIsValid<T>(childPointer)) {
324 throw std::runtime_error("Missing required child node: " + std::string(key));
325 }
326
327 return T(m_root, childPointer);
328 }
329
337 template <typename T>
338 T setChild(const std::string_view& key, const T& value)
339 {
340 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
341
342 json_pointer childPointer = m_pointer / std::string(key);
343 (*m_root)[childPointer] = value.ref();
344 return T(m_root, childPointer);
345 }
346
354 template <typename T>
355 std::optional<T> getOptionalChild(const std::string_view& key) const
356 {
357 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
358
359 json_pointer childPointer = m_pointer / std::string(key);
360 if (!checkKeyIsValid<T>(childPointer)) {
361 return std::nullopt;
362 }
363
364 return T(m_root, childPointer);
365 }
366
367private:
375 template <typename T>
376 bool checkKeyIsValid(const json_pointer& pointer) const
377 {
378 if (!(*m_root).contains(pointer)) {
379 return false;
380 }
381
382 const json& node = (*m_root).at(pointer);
383
384 if constexpr (std::is_base_of_v<Object, T>) {
385 if (!node.is_object()) {
386 throw std::runtime_error("Expected an object for: " + pointer.to_string());
387 }
388 } else if constexpr (std::is_base_of_v<Array<typename T::value_type>, T>) {
389 if (!node.is_array()) {
390 throw std::runtime_error("Expected an array for: " + pointer.to_string());
391 }
392 }
393
394 return true;
395 }
396
402 json& resolve_pointer() const
403 {
404 return (*m_root).at(m_pointer); // Throws if invalid
405 }
406
407 const std::shared_ptr<json> m_root;
408 json_pointer m_pointer;
409
410 friend class validation::SemanticValidator;
411};
412
414using ErrorHandler = std::function<void(const std::string& message, const Base& location)>;
415
419class Object : public Base
420{
421public:
425 Object(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
426 {
427 if (!ref().is_object()) {
428 throw std::invalid_argument("mnx::Object must wrap a JSON object.");
429 }
430 }
431
435 Object(Base& parent, const std::string_view& key)
436 : Base(json::object(), parent, key) {}
437};
438
441template <typename T, std::enable_if_t<!std::is_base_of_v<Base, T>, int> = 0>
442class SimpleType : public Base
443{
444 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>, "This template is for simple JSON classes");
445
446public:
447 using value_type = T;
448
452 SimpleType(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
453 {
454 }
455
457 operator T() const
458 {
459 return ref().template get<T>();
460 }
461
464 SimpleType& operator=(const T& src)
465 {
466 ref() = src;
467 return *this;
468 }
469
471 bool operator==(const T& src) const
472 {
473 return src == ref().template get<T>();
474 }
475};
476
477class ArrayElementObject;
481template <typename T>
482class Array : public Base
483{
484 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
485 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX array element type.");
486
487private:
488 template<typename ArrayType>
489 struct iter
490 {
491 private:
492 ArrayType* m_ptr;
493 mutable size_t m_idx;
494
495 public:
496 iter(ArrayType* ptr, size_t idx) : m_ptr(ptr), m_idx(idx) {}
497 T operator*() const { return (*m_ptr)[m_idx]; }
498 iter& operator++() { ++m_idx; return *this; }
499 bool operator!=(const iter& o) const { return m_idx != o.m_idx; }
500 };
501
502public:
504 using value_type = T;
505
506 using iterator = iter<Array>;
507 using const_iterator = iter<const Array>;
508
512 Array(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
513 {
514 if (!ref().is_array()) {
515 throw std::invalid_argument("mnx::Array must wrap a JSON array.");
516 }
517 }
518
522 Array(Base& parent, const std::string_view& key)
523 : Base(json::array(), parent, key) {}
524
526 size_t size() const { return ref().size(); }
527
529 bool empty() const { return ref().empty(); }
530
532 void clear() { ref().clear(); }
533
535 auto operator[](size_t index) const
536 {
537 checkIndex(index);
538 if constexpr (std::is_base_of_v<Base, T>) {
539 return getChild<T>(std::to_string(index));
540 } else {
541 return getChild<SimpleType<T>>(std::to_string(index));
542 }
543 }
544
546 auto operator[](size_t index)
547 {
548 checkIndex(index);
549 if constexpr (std::is_base_of_v<Base, T>) {
550 return getChild<T>(std::to_string(index));
551 } else {
552 return getChild<SimpleType<T>>(std::to_string(index));
553 }
554 }
555
557 template <typename U = T>
558 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
559 push_back(const U& value)
560 {
561 ref().push_back(value);
562 }
563
568 template <typename U = T, typename... Args,
569 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
570 U append(Args&&... args)
571 {
572 if constexpr (std::is_base_of_v<Object, U>) {
573 ref().push_back(json::object());
574 } else {
575 ref().push_back(json::array());
576 }
577 return U(*this, std::to_string(ref().size() - 1), std::forward<Args>(args)...);
578 }
579
581 void erase(size_t index)
582 {
583 checkIndex(index);
584 ref().erase(ref().begin() + index);
585 }
586
588 auto begin() { return iterator(this, 0); }
589
591 auto end() { return iterator(this, size()); }
592
594 auto begin() const { return const_iterator(this, 0); }
595
597 auto end() const { return const_iterator(this, size()); }
598
599protected:
602 void checkIndex(size_t index) const
603 {
604 assert(index < ref().size());
605 if (index >= ref().size()) {
606 throw std::out_of_range("Index out of range");
607 }
608 }
609};
610
615{
616public:
617 using Object::Object;
618
620 size_t calcArrayIndex() const
621 {
622 return std::stoul(pointer().back());
623 }
624
630 template <typename ContainerType>
631 ContainerType container() const
632 {
633 return parent<Array<ArrayElementObject>>().parent<ContainerType>();
634 }
635};
636
637class ContentArray;
640{
641protected:
642 static constexpr std::string_view ContentTypeValueDefault = "event";
643
644public:
645 using ArrayElementObject::ArrayElementObject;
646
648
650 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
651 T get() const
652 {
653 return getTypedObject<T>();
654 }
655
656private:
659 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
660 T getTypedObject() const
661 {
662 if (type() != T::ContentTypeValue) {
663 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
664 ", got " + type());
665 }
666 return T(root(), pointer());
667 }
668
669 friend class ContentArray;
670};
671
699class ContentArray : public Array<ContentObject>
700{
701public:
703 using BaseArray::BaseArray; // Inherit constructors
704
706 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
707 T get(size_t index) const
708 {
709 this->checkIndex(index);
710 return operator[](index).get<T>();
711 }
712
714 template <typename T, typename... Args,
715 std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
716 T append(Args&&... args)
717 {
718 auto result = BaseArray::append<T>(std::forward<Args>(args)...);
719 if constexpr (T::ContentTypeValue != ContentObject::ContentTypeValueDefault) {
720 result.set_type(std::string(T::ContentTypeValue));
721 }
722 return result;
723 }
724
725private:
728 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
729 T getTypedObject(size_t index) const
730 {
731 this->checkIndex(index);
732 auto element = (*this)[index];
733 if (element.type() != T::ContentTypeValue) {
734 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
735 ", got " + element.type());
736 }
737 return T(root(), pointer() / std::to_string(index));
738 }
739};
740
745template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
747{
748 static const std::unordered_map<std::string, E> stringToEnum();
749
751 static const std::unordered_map<E, std::string> enumToString()
752 {
753 static const std::unordered_map<E, std::string> reverseMap = []() {
754 std::unordered_map<E, std::string> result;
755 for (const auto& element : EnumStringMapping<E>::stringToEnum()) {
756 result.emplace(element.second, element.first);
757 }
758 return result;
759 }();
760 return reverseMap;
761 }
762};
763
767template <typename T>
768class Dictionary : public Object
769{
770 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
771 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX dictionary element type.");
772
773private:
774 template <typename DictionaryType, typename IteratorType>
775 struct iter
776 {
777 using value_type = std::pair<const std::string, T>;
778 using difference_type = std::ptrdiff_t;
779 using iterator_category = std::forward_iterator_tag;
780 using pointer = value_type*;
781 using reference = value_type&;
782
783 private:
784 DictionaryType* m_ptr;
785 IteratorType m_it;
786 mutable std::unique_ptr<value_type> m_pair; // Cached key-value pair for operator*() and operator->()
787
788 void update_pair() const {
789 m_pair.reset();
790 if (m_it != m_ptr->ref().end()) {
791 m_pair = std::make_unique<value_type>(m_it.key(), m_ptr->operator[](m_it.key()));
792 }
793 }
794
795 public:
796 iter(DictionaryType* ptr, IteratorType it) : m_ptr(ptr), m_it(it)
797 { update_pair(); }
798
799 reference operator*() const { return *m_pair.get(); }
800 pointer operator->() const { return m_pair.get(); }
801
802 iter& operator++() { ++m_it; update_pair(); return *this; }
803 iter operator++(int) { iter tmp = *this; ++(*this); return tmp; }
804
805 bool operator!=(const iter& o) const { return m_it != o.m_it; }
806 bool operator==(const iter& o) const { return m_it == o.m_it; }
807 };
808
809public:
811 using value_type = T;
812
813 using iterator = iter<Dictionary, json::iterator>;
814 using const_iterator = iter<const Dictionary, json::const_iterator>;
815
819 Dictionary(const std::shared_ptr<json>& root, json_pointer pointer)
821 {
822 }
823
827 Dictionary(Base& parent, const std::string_view& key)
828 : Object(parent, key) {}
829
831 size_t size() const { return ref().size(); }
832
834 bool empty() const { return ref().empty(); }
835
837 void clear() { ref().clear(); }
838
840 T operator[](const std::string& key) const
841 {
842 if constexpr (std::is_base_of_v<Base, T>) {
843 return getChild<T>(key);
844 } else {
845 return getChild<SimpleType<T>>(key);
846 }
847 }
848
850 auto operator[](const std::string& key)
851 {
852 if constexpr (std::is_base_of_v<Base, T>) {
853 return getChild<T>(key);
854 } else {
855 return getChild<SimpleType<T>>(key);
856 }
857 }
858
860 template <typename U = T>
861 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
862 emplace(const std::string& key, const U& value)
863 {
864 ref()[key] = value;
865 }
866
871 template <typename U = T, typename... Args,
872 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
873 U append(const std::string& key, Args&&... args)
874 {
875 if constexpr (std::is_base_of_v<Object, U>) {
876 ref()[key] = json::object();
877 } else {
878 ref()[key] = json::array();
879 }
880 return U(*this, key, std::forward<Args>(args)...);
881 }
882
884 void erase(const std::string& key)
885 {
886 ref().erase(key);
887 }
888
892 auto find(const std::string& key)
893 {
894 auto it = ref().find(key);
895 return (it != ref().end()) ? iterator(this, it) : end();
896 }
897
901 auto find(const std::string& key) const
902 {
903 auto it = ref().find(key);
904 return (it != ref().end()) ? const_iterator(this, it) : end();
905 }
906
908 auto begin() { return iterator(this, ref().begin()); }
909
911 auto end() { return iterator(this, ref().end()); }
912
914 auto begin() const { return const_iterator(this, ref().begin()); }
915
917 auto end() const { return const_iterator(this, ref().end()); }
918};
919
920} // namespace mnx
921
922#ifndef DOXYGEN_SHOULD_IGNORE_THIS
923
924namespace nlohmann {
925
926#if defined(_WIN32)
927// This general adl_serializer is enabled only for enum types.
928// For some reason MSC does not like the direct function definitions below.
929template<typename EnumType>
930struct adl_serializer<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
931{
932 template<typename BasicJsonType>
933 static EnumType from_json(const BasicJsonType& j)
934 {
935 // Lookup the string in the specialized map.
937 auto it = map.find(j.get<std::string>());
938 if (it != map.end()) {
939 return it->second;
940 }
942 return EnumType{};
943 }
944
945 template<typename BasicJsonType>
946 static void to_json(BasicJsonType& j, const EnumType& value)
947 {
949 auto it = map.find(value);
950 if (it == map.end()) {
952 j = BasicJsonType();
953 return;
954 }
955 j = it->second;
956 }
957};
958#else
959// Clang works with the adl_specialization above, but GCC does not.
960namespace detail {
961
962template<typename BasicJsonType, typename EnumType,
963 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
964inline void from_json(const BasicJsonType& j, EnumType& value)
965{
966 // Lookup the string in the specialized map.
968 auto it = map.find(j.template get<std::string>());
969 if (it != map.end()) {
970 value = it->second;
971 } else {
973 value = EnumType{};
974 }
975}
976
977template<typename BasicJsonType, typename EnumType,
978 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
979inline void to_json(BasicJsonType& j, EnumType value) noexcept
980{
982 auto it = map.find(value);
983 if (it != map.end()) {
984 j = it->second;
985 } else {
987 j = BasicJsonType();
988 }
989}
990
991} // namespace detail
992#endif // defined(_WIN32)
993
994} // namespace nlohmann
995
996#endif // DOXYGEN_SHOULD_IGNORE_THIS
Represents an MNX object that is included as an array element.
Definition BaseTypes.h:615
ContainerType container() const
Returns the container of the array this element belongs to wrapped as the specified template type.
Definition BaseTypes.h:631
size_t calcArrayIndex() const
Calculates the array index of the current instance within the array.
Definition BaseTypes.h:620
Represents an MNX array, encapsulating property access.
Definition BaseTypes.h:483
void checkIndex(size_t index) const
validates that an index is not out of range
Definition BaseTypes.h:602
auto begin()
Returns an iterator to the beginning of the array.
Definition BaseTypes.h:588
size_t size() const
Get the size of the array.
Definition BaseTypes.h:526
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:529
iter< Array > iterator
non-const iterator type
Definition BaseTypes.h:506
iter< const Array > const_iterator
const iterator type
Definition BaseTypes.h:507
T value_type
The type for elements in this Array.
Definition BaseTypes.h:504
auto operator[](size_t index) const
const operator[]
Definition BaseTypes.h:535
auto begin() const
Returns a const iterator to the beginning of the array.
Definition BaseTypes.h:594
void erase(size_t index)
Remove an element at a given index.
Definition BaseTypes.h:581
auto end()
Returns an iterator to the end of the array.
Definition BaseTypes.h:591
void clear()
Clear all elements.
Definition BaseTypes.h:532
Array(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Array class around an existing JSON array node.
Definition BaseTypes.h:512
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:559
U append(Args &&... args)
Create a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:570
auto end() const
Returns a const iterator to the end of the array.
Definition BaseTypes.h:597
auto operator[](size_t index)
non-const operator[]
Definition BaseTypes.h:546
Array(Base &parent, const std::string_view &key)
Creates a new Array class as a child of a JSON node.
Definition BaseTypes.h:522
Base class wrapper for all MNX JSON nodes.
Definition BaseTypes.h:210
json & ref() const
Convert this node for retrieval.
Definition BaseTypes.h:279
std::optional< T > getEnclosingElement() const
Returns the enclosing array element for this instance.
Definition Implementations.cpp:54
std::string dump(int indents=-1)
Dumps the branch to a string. Useful in debugging.
Definition BaseTypes.h:249
T getChild(const std::string_view &key) const
Retrieves and validates a required child node.
Definition BaseTypes.h:318
json_pointer pointer() const
Returns the json_pointer for this node.
Definition BaseTypes.h:271
Base(json &&jsonRef, Base &parent, const std::string_view &key)
Construct a Base reference as a child inside a parent node.
Definition BaseTypes.h:304
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:295
T setChild(const std::string_view &key, const T &value)
Sets a child node.
Definition BaseTypes.h:338
T parent() const
Returns the parent object for this node.
Definition BaseTypes.h:258
json & ref()
Access the JSON node for modification.
Definition BaseTypes.h:285
std::optional< T > getOptionalChild(const std::string_view &key) const
Retrieves an optional child node.
Definition BaseTypes.h:355
Base(const Base &src)
Copy constructor.
Definition BaseTypes.h:215
Base & operator=(const Base &src)
Copy assignment operator.
Definition BaseTypes.h:224
Base(Base &&src) noexcept
Move constructor.
Definition BaseTypes.h:219
Base & operator=(Base &&src)
Move assignment operator.
Definition BaseTypes.h:236
const std::shared_ptr< json > & root() const
Returns the root.
Definition BaseTypes.h:288
Class for content arrays.
Definition BaseTypes.h:700
T get(size_t index) const
Retrieve an element from the array as a specific type.
Definition BaseTypes.h:707
T append(Args &&... args)
Append an element of the specified type.
Definition BaseTypes.h:716
Base class for objects that are elements of content arrays.
Definition BaseTypes.h:640
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:642
T get() const
Retrieve an element as a specific type.
Definition BaseTypes.h:651
Represents an MNX dictionary, where each key is a user-defined string.
Definition BaseTypes.h:769
iter< Dictionary, json::iterator > iterator
non-const iterator type
Definition BaseTypes.h:813
auto operator[](const std::string &key)
non-const operator[]
Definition BaseTypes.h:850
size_t size() const
Get the size of the array.
Definition BaseTypes.h:831
auto end() const
Returns a const iterator to the end of the dictionary.
Definition BaseTypes.h:917
auto begin() const
Returns a const iterator to the beginning of the dictionary.
Definition BaseTypes.h:914
auto find(const std::string &key) const
Finds an element by key and returns a const iterator.
Definition BaseTypes.h:901
U append(const std::string &key, Args &&... args)
Create a new element using the input key. (Available only for Base types)
Definition BaseTypes.h:873
void erase(const std::string &key)
Remove an element at a given key.
Definition BaseTypes.h:884
iter< const Dictionary, json::const_iterator > const_iterator
const iterator type
Definition BaseTypes.h:814
T operator[](const std::string &key) const
const operator[]
Definition BaseTypes.h:840
T value_type
The type for elements in this Array.
Definition BaseTypes.h:811
auto begin()
Returns an iterator to the beginning of the dictionary.
Definition BaseTypes.h:908
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:834
std::enable_if_t<!std::is_base_of_v< Base, U >, void > emplace(const std::string &key, const U &value)
Add a new value to the dictonary. (Available only for primitive types)
Definition BaseTypes.h:862
Dictionary(Base &parent, const std::string_view &key)
Creates a new Dictionary class as a child of a JSON node.
Definition BaseTypes.h:827
Dictionary(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Dictionary class around an existing JSON node.
Definition BaseTypes.h:819
auto end()
Returns an iterator to the end of the dictionary.
Definition BaseTypes.h:911
auto find(const std::string &key)
Finds an element by key and returns an iterator.
Definition BaseTypes.h:892
void clear()
Clear all elements.
Definition BaseTypes.h:837
Represents an MNX object, encapsulating property access.
Definition BaseTypes.h:420
Object(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Object class around an existing JSON object node.
Definition BaseTypes.h:425
Object(Base &parent, const std::string_view &key)
Creates a new Object class as a child of a JSON node.
Definition BaseTypes.h:435
Allows access to a fundamental type (number, boolean, string) in a JSON node.
Definition BaseTypes.h:443
SimpleType(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps a SimpleType class around an existing JSON object node.
Definition BaseTypes.h:452
bool operator==(const T &src) const
Equality comparison with value type.
Definition BaseTypes.h:471
T value_type
value type of this SimpleType
Definition BaseTypes.h:447
SimpleType & operator=(const T &src)
Allow assignment to underlying json reference.
Definition BaseTypes.h:464
object model for MNX format
std::function< void(const std::string &message, const Base &location)> ErrorHandler
Error handler type for reporting errors.
Definition BaseTypes.h:414
json::json_pointer json_pointer
JSON pointer class for MNX.
Definition BaseTypes.h:197
constexpr int MNX_VERSION
The MNX version for files generated by the DOM.
Definition BaseTypes.h:194
nlohmann::ordered_json json
JSON class for MNX.
Definition BaseTypes.h:196
Supplies enum string mappings to nlohmann json's serializer.
Definition BaseTypes.h:747
static const std::unordered_map< E, std::string > enumToString()
maps enum values to strings
Definition BaseTypes.h:751
static const std::unordered_map< std::string, E > stringToEnum()
maps strings to enum values