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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] std::optional<TYPE> NAME() const { \
90 return ref().contains(KEY) ? std::optional<TYPE>(ref()[KEY].get<TYPE>()) : std::nullopt; \
91 } \
92 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] std::optional<TYPE> NAME() const { return getOptionalChild<TYPE>(#NAME); } \
174 template<typename... Args> \
175 TYPE create_##NAME(Args&&... args) { \
176 if (auto child = getOptionalChild<TYPE>(#NAME)) return child.value(); \
177 return setChild(#NAME, TYPE(*this, #NAME, std::forward<Args>(args)...)); \
178 } \
179 void clear_##NAME() { ref().erase(#NAME); } \
180 static_assert(true, "") // require semicolon after macro
181
182#define MNX_ASSERT_IF(TEST) \
183assert(!(TEST)); \
184if (TEST)
185
192namespace mnx {
193
195inline constexpr int MNX_VERSION = 1;
196
197using json = nlohmann::ordered_json;
198using json_pointer = json::json_pointer;
199
200class Object;
201class Document;
202template <typename T> class Array;
203
204namespace validation {
205class SemanticValidator;
206}; // namespace validation
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>
271 [[nodiscard]] std::optional<T> getEnclosingElement() const;
272
274 [[nodiscard]] json_pointer pointer() const { return m_pointer; }
275
277 [[nodiscard]] Document document() const;
278
279protected:
285 [[nodiscard]] json& ref() const { return resolve_pointer(); }
286
291 [[nodiscard]] json& ref() { return resolve_pointer(); }
292
294 [[nodiscard]] const std::shared_ptr<json>& root() const { return m_root; }
295
301 Base(const std::shared_ptr<json>& root, json_pointer pointer)
302 : m_root(root), m_pointer(std::move(pointer)) {}
303
310 Base(json&& jsonRef, Base& parent, std::string_view key)
311 : m_root(parent.m_root), m_pointer(parent.m_pointer / std::string(key))
312 {
313 (*m_root)[m_pointer] = std::move(jsonRef);
314 }
315
323 template <typename T>
324 [[nodiscard]] T getChild(std::string_view key) const
325 {
326 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
327
328 json_pointer childPointer = m_pointer / std::string(key);
329 if (!checkKeyIsValid<T>(childPointer)) {
330 throw std::runtime_error("Missing required child node: " + std::string(key));
331 }
332
333 return T(m_root, childPointer);
334 }
335
343 template <typename T>
344 T setChild(std::string_view key, const T& value)
345 {
346 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
347
348 json_pointer childPointer = m_pointer / std::string(key);
349 (*m_root)[childPointer] = value.ref();
350 return T(m_root, childPointer);
351 }
352
360 template <typename T>
361 [[nodiscard]] std::optional<T> getOptionalChild(std::string_view key) const
362 {
363 static_assert(std::is_base_of_v<Base, T>, "template type must be derived from Base");
364
365 json_pointer childPointer = m_pointer / std::string(key);
366 if (!checkKeyIsValid<T>(childPointer)) {
367 return std::nullopt;
368 }
369
370 return T(m_root, childPointer);
371 }
372
373private:
381 template <typename T>
382 [[nodiscard]] bool checkKeyIsValid(const json_pointer& pointer) const
383 {
384 if (!(*m_root).contains(pointer)) {
385 return false;
386 }
387
388 const json& node = (*m_root).at(pointer);
389
390 if constexpr (std::is_base_of_v<Object, T>) {
391 if (!node.is_object()) {
392 throw std::runtime_error("Expected an object for: " + pointer.to_string());
393 }
394 } else if constexpr (std::is_base_of_v<Array<typename T::value_type>, T>) {
395 if (!node.is_array()) {
396 throw std::runtime_error("Expected an array for: " + pointer.to_string());
397 }
398 }
399
400 return true;
401 }
402
408 [[nodiscard]] json& resolve_pointer() const
409 {
410 return (*m_root).at(m_pointer); // Throws if invalid
411 }
412
413 const std::shared_ptr<json> m_root;
414 json_pointer m_pointer;
415
416 friend class validation::SemanticValidator;
417};
418
420using ErrorHandler = std::function<void(const std::string& message, const Base& location)>;
421
425class Object : public Base
426{
427public:
431 Object(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
432 {
433 if (!ref().is_object()) {
434 throw std::invalid_argument("mnx::Object must wrap a JSON object.");
435 }
436 }
437
441 Object(Base& parent, std::string_view key)
442 : Base(json::object(), parent, key) {}
443
444 MNX_OPTIONAL_PROPERTY(std::string, _c);
446 MNX_OPTIONAL_PROPERTY(std::string, id);
447};
448
451template <typename T, std::enable_if_t<!std::is_base_of_v<Base, T>, int> = 0>
452class SimpleType : public Base
453{
454 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>, "This template is for simple JSON classes");
455
456public:
457 using value_type = T;
458
462 SimpleType(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
463 {
464 }
465
467 operator T() const
468 {
469 return ref().template get<T>();
470 }
471
474 SimpleType& operator=(const T& src)
475 {
476 ref() = src;
477 return *this;
478 }
479
481 bool operator==(const T& src) const
482 {
483 return src == ref().template get<T>();
484 }
485};
486
487class ArrayElementObject;
491template <typename T>
492class Array : public Base
493{
494 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
495 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX array element type.");
496
497private:
498 template<typename ArrayType>
499 struct iter
500 {
501 private:
502 ArrayType* m_ptr;
503 mutable size_t m_idx;
504
505 public:
506 iter(ArrayType* ptr, size_t idx) : m_ptr(ptr), m_idx(idx) {}
507 T operator*() const { return (*m_ptr)[m_idx]; }
508 iter& operator++() { ++m_idx; return *this; }
509 bool operator!=(const iter& o) const { return m_idx != o.m_idx; }
510 };
511
512public:
514 using value_type = T;
515
516 using iterator = iter<Array>;
517 using const_iterator = iter<const Array>;
518
522 Array(const std::shared_ptr<json>& root, json_pointer pointer) : Base(root, pointer)
523 {
524 if (!ref().is_array()) {
525 throw std::invalid_argument("mnx::Array must wrap a JSON array.");
526 }
527 }
528
532 Array(Base& parent, std::string_view key)
533 : Base(json::array(), parent, key) {}
534
536 [[nodiscard]] size_t size() const { return ref().size(); }
537
539 [[nodiscard]] bool empty() const { return ref().empty(); }
540
542 void clear() { ref().clear(); }
543
545 [[nodiscard]] T at(size_t index) const
546 {
547 return operator[](index);
548 }
549
551 [[nodiscard]] auto operator[](size_t index) const
552 {
553 checkIndex(index);
554 if constexpr (std::is_base_of_v<Base, T>) {
555 return getChild<T>(std::to_string(index));
556 } else {
557 return getChild<SimpleType<T>>(std::to_string(index));
558 }
559 }
560
562 [[nodiscard]] auto operator[](size_t index)
563 {
564 checkIndex(index);
565 if constexpr (std::is_base_of_v<Base, T>) {
566 return getChild<T>(std::to_string(index));
567 } else {
568 return getChild<SimpleType<T>>(std::to_string(index));
569 }
570 }
571
573 template <typename U = T>
574 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
575 push_back(const U& value)
576 {
577 ref().push_back(value);
578 }
579
584 template <typename U = T, typename... Args,
585 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
586 U append(Args&&... args)
587 {
588 if constexpr (std::is_base_of_v<Object, U>) {
589 ref().push_back(json::object());
590 } else {
591 ref().push_back(json::array());
592 }
593 return U(*this, std::to_string(ref().size() - 1), std::forward<Args>(args)...);
594 }
595
597 void erase(size_t index)
598 {
599 checkIndex(index);
600 ref().erase(ref().begin() + index);
601 }
602
604 [[nodiscard]] auto begin() { return iterator(this, 0); }
605
607 [[nodiscard]] auto end() { return iterator(this, size()); }
608
610 [[nodiscard]] auto begin() const { return const_iterator(this, 0); }
611
613 [[nodiscard]] auto end() const { return const_iterator(this, size()); }
614
615protected:
618 void checkIndex(size_t index) const
619 {
620 assert(index < ref().size());
621 if (index >= ref().size()) {
622 throw std::out_of_range("Index out of range");
623 }
624 }
625};
626
631{
632public:
633 using Object::Object;
634
636 size_t calcArrayIndex() const
637 {
638 return std::stoul(pointer().back());
639 }
640
646 template <typename ContainerType>
647 ContainerType container() const
648 {
649 return parent<Array<ArrayElementObject>>().parent<ContainerType>();
650 }
651};
652
653class ContentArray;
656{
657protected:
658 static constexpr std::string_view ContentTypeValueDefault = "event";
659
660public:
661 using ArrayElementObject::ArrayElementObject;
662
664
666 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
667 [[nodiscard]] T get() const
668 {
669 return getTypedObject<T>();
670 }
671
672private:
675 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
676 [[nodiscard]] T getTypedObject() const
677 {
678 if (type() != T::ContentTypeValue) {
679 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
680 ", got " + type());
681 }
682 return T(root(), pointer());
683 }
684
685 friend class ContentArray;
686};
687
715class ContentArray : public Array<ContentObject>
716{
717public:
719 using BaseArray::BaseArray; // Inherit constructors
720
722 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
723 [[nodiscard]] T get(size_t index) const
724 {
725 this->checkIndex(index);
726 return operator[](index).get<T>();
727 }
728
730 template <typename T, typename... Args,
731 std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
732 T append(Args&&... args)
733 {
734 auto result = BaseArray::append<T>(std::forward<Args>(args)...);
735 if constexpr (T::ContentTypeValue != ContentObject::ContentTypeValueDefault) {
736 result.set_type(std::string(T::ContentTypeValue));
737 }
738 return result;
739 }
740
741private:
744 template <typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>, int> = 0>
745 [[nodiscard]] T getTypedObject(size_t index) const
746 {
747 this->checkIndex(index);
748 auto element = (*this)[index];
749 if (element.type() != T::ContentTypeValue) {
750 throw std::invalid_argument("Type mismatch: expected " + std::string(T::ContentTypeValue) +
751 ", got " + element.type());
752 }
753 return T(root(), pointer() / std::to_string(index));
754 }
755};
756
761template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
763{
764 static const std::unordered_map<std::string, E> stringToEnum();
765
767 static const std::unordered_map<E, std::string> enumToString()
768 {
769 static const std::unordered_map<E, std::string> reverseMap = []() {
770 std::unordered_map<E, std::string> result;
771 for (const auto& element : EnumStringMapping<E>::stringToEnum()) {
772 result.emplace(element.second, element.first);
773 }
774 return result;
775 }();
776 return reverseMap;
777 }
778};
779
783template <typename T>
784class Dictionary : public Object
785{
786 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||
787 std::is_base_of_v<ArrayElementObject, T>, "Invalid MNX dictionary element type.");
788
789private:
790 template <typename DictionaryType, typename IteratorType>
791 struct iter
792 {
793 using value_type = std::pair<const std::string, T>;
794 using difference_type = std::ptrdiff_t;
795 using iterator_category = std::forward_iterator_tag;
796 using pointer = value_type*;
797 using reference = value_type&;
798
799 private:
800 DictionaryType* m_ptr;
801 IteratorType m_it;
802 mutable std::unique_ptr<value_type> m_pair; // Cached key-value pair for operator*() and operator->()
803
804 void update_pair() const {
805 m_pair.reset();
806 if (m_it != m_ptr->ref().end()) {
807 m_pair = std::make_unique<value_type>(m_it.key(), m_ptr->operator[](m_it.key()));
808 }
809 }
810
811 public:
812 iter(DictionaryType* ptr, IteratorType it) : m_ptr(ptr), m_it(it)
813 { update_pair(); }
814
815 [[nodiscard]] reference operator*() const { return *m_pair.get(); }
816 [[nodiscard]] pointer operator->() const { return m_pair.get(); }
817
818 iter& operator++() { ++m_it; update_pair(); return *this; }
819 iter operator++(int) { iter tmp = *this; ++(*this); return tmp; }
820
821 [[nodiscard]] bool operator!=(const iter& o) const { return m_it != o.m_it; }
822 [[nodiscard]] bool operator==(const iter& o) const { return m_it == o.m_it; }
823 };
824
825public:
827 using value_type = T;
828
829 using iterator = iter<Dictionary, json::iterator>;
830 using const_iterator = iter<const Dictionary, json::const_iterator>;
831
835 Dictionary(const std::shared_ptr<json>& root, json_pointer pointer)
837 {
838 }
839
843 Dictionary(Base& parent, std::string_view key)
844 : Object(parent, key) {}
845
847 [[nodiscard]] size_t size() const { return ref().size(); }
848
850 [[nodiscard]] bool empty() const { return ref().empty(); }
851
853 void clear() { ref().clear(); }
854
856 [[nodiscard]] T at(size_t index) const
857 {
858 return operator[](index);
859 }
860
862 [[nodiscard]] auto operator[](const std::string& key) const
863 {
864 if constexpr (std::is_base_of_v<Base, T>) {
865 return getChild<T>(key);
866 } else {
867 return getChild<SimpleType<T>>(key);
868 }
869 }
870
872 [[nodiscard]] auto operator[](const std::string& key)
873 {
874 if constexpr (std::is_base_of_v<Base, T>) {
875 return getChild<T>(key);
876 } else {
877 return getChild<SimpleType<T>>(key);
878 }
879 }
880
882 template <typename U = T>
883 std::enable_if_t<!std::is_base_of_v<Base, U>, void>
884 emplace(const std::string& key, const U& value)
885 {
886 ref()[key] = value;
887 }
888
893 template <typename U = T, typename... Args,
894 std::enable_if_t<std::is_base_of_v<Base, U>, int> = 0>
895 U append(const std::string& key, Args&&... args)
896 {
897 if constexpr (std::is_base_of_v<Object, U>) {
898 ref()[key] = json::object();
899 } else {
900 ref()[key] = json::array();
901 }
902 return U(*this, key, std::forward<Args>(args)...);
903 }
904
906 void erase(const std::string& key)
907 {
908 ref().erase(key);
909 }
910
914 [[nodiscard]] auto find(const std::string& key)
915 {
916 auto it = ref().find(key);
917 return (it != ref().end()) ? iterator(this, it) : end();
918 }
919
923 [[nodiscard]] auto find(const std::string& key) const
924 {
925 auto it = ref().find(key);
926 return (it != ref().end()) ? const_iterator(this, it) : end();
927 }
928
931 [[nodiscard]] bool contains(const std::string& key) const
932 { return find(key) != end(); }
933
935 [[nodiscard]] auto begin() { return iterator(this, ref().begin()); }
936
938 [[nodiscard]] auto end() { return iterator(this, ref().end()); }
939
941 [[nodiscard]] auto begin() const { return const_iterator(this, ref().begin()); }
942
944 [[nodiscard]] auto end() const { return const_iterator(this, ref().end()); }
945};
946
947} // namespace mnx
948
949#ifndef DOXYGEN_SHOULD_IGNORE_THIS
950
951namespace nlohmann {
952
953#if defined(_WIN32)
954// This general adl_serializer is enabled only for enum types.
955// For some reason MSC does not like the direct function definitions below.
956template<typename EnumType>
957struct adl_serializer<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
958{
959 template<typename BasicJsonType>
960 static EnumType from_json(const BasicJsonType& j)
961 {
962 // Lookup the string in the specialized map.
964 auto it = map.find(j.get<std::string>());
965 if (it != map.end()) {
966 return it->second;
967 }
969 return EnumType{};
970 }
971
972 template<typename BasicJsonType>
973 static void to_json(BasicJsonType& j, const EnumType& value)
974 {
976 auto it = map.find(value);
977 if (it == map.end()) {
979 j = BasicJsonType();
980 return;
981 }
982 j = it->second;
983 }
984};
985#else
986// Clang works with the adl_specialization above, but GCC does not.
987namespace detail {
988
989template<typename BasicJsonType, typename EnumType,
990 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
991inline void from_json(const BasicJsonType& j, EnumType& value)
992{
993 // Lookup the string in the specialized map.
995 auto it = map.find(j.template get<std::string>());
996 if (it != map.end()) {
997 value = it->second;
998 } else {
1000 value = EnumType{};
1001 }
1002}
1003
1004template<typename BasicJsonType, typename EnumType,
1005 std::enable_if_t<std::is_enum<EnumType>::value, int> = 0>
1006inline void to_json(BasicJsonType& j, EnumType value) noexcept
1007{
1009 auto it = map.find(value);
1010 if (it != map.end()) {
1011 j = it->second;
1012 } else {
1014 j = BasicJsonType();
1015 }
1016}
1017
1018} // namespace detail
1019#endif // defined(_WIN32)
1020
1021} // namespace nlohmann
1022
1023#endif // DOXYGEN_SHOULD_IGNORE_THIS
Represents an MNX object that is included as an array element.
Definition BaseTypes.h:631
ContainerType container() const
Returns the container of the array this element belongs to wrapped as the specified template type.
Definition BaseTypes.h:647
size_t calcArrayIndex() const
Calculates the array index of the current instance within the array.
Definition BaseTypes.h:636
Represents an MNX array, encapsulating property access.
Definition BaseTypes.h:493
void checkIndex(size_t index) const
validates that an index is not out of range
Definition BaseTypes.h:618
auto begin()
Returns an iterator to the beginning of the array.
Definition BaseTypes.h:604
size_t size() const
Get the size of the array.
Definition BaseTypes.h:536
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:539
iter< Array > iterator
non-const iterator type
Definition BaseTypes.h:516
iter< const Array > const_iterator
const iterator type
Definition BaseTypes.h:517
T value_type
The type for elements in this Array.
Definition BaseTypes.h:514
auto operator[](size_t index) const
const operator[]
Definition BaseTypes.h:551
Array(Base &parent, std::string_view key)
Creates a new Array class as a child of a JSON node.
Definition BaseTypes.h:532
auto begin() const
Returns a const iterator to the beginning of the array.
Definition BaseTypes.h:610
void erase(size_t index)
Remove an element at a given index.
Definition BaseTypes.h:597
auto end()
Returns an iterator to the end of the array.
Definition BaseTypes.h:607
void clear()
Clear all elements.
Definition BaseTypes.h:542
Array(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Array class around an existing JSON array node.
Definition BaseTypes.h:522
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:575
U append(Args &&... args)
Create a new element at the end of the array. (Available only for Base types)
Definition BaseTypes.h:586
T at(size_t index) const
Direct getter for a particular element.
Definition BaseTypes.h:545
auto end() const
Returns a const iterator to the end of the array.
Definition BaseTypes.h:613
auto operator[](size_t index)
non-const operator[]
Definition BaseTypes.h:562
Base class wrapper for all MNX JSON nodes.
Definition BaseTypes.h:212
json & ref() const
Convert this node for retrieval.
Definition BaseTypes.h:285
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:67
Base(json &&jsonRef, Base &parent, std::string_view key)
Construct a Base reference as a child inside a parent node.
Definition BaseTypes.h:310
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:301
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:324
json & ref()
Access the JSON node for modification.
Definition BaseTypes.h:291
std::optional< T > getOptionalChild(std::string_view key) const
Retrieves an optional child node.
Definition BaseTypes.h:361
T setChild(std::string_view key, const T &value)
Sets a child node.
Definition BaseTypes.h:344
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:118
const std::shared_ptr< json > & root() const
Returns the root.
Definition BaseTypes.h:294
Class for content arrays.
Definition BaseTypes.h:716
T get(size_t index) const
Retrieve an element from the array as a specific type.
Definition BaseTypes.h:723
T append(Args &&... args)
Append an element of the specified type.
Definition BaseTypes.h:732
Base class for objects that are elements of content arrays.
Definition BaseTypes.h:656
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:658
T get() const
Retrieve an element as a specific type.
Definition BaseTypes.h:667
Represents an MNX dictionary, where each key is a user-defined string.
Definition BaseTypes.h:785
iter< Dictionary, json::iterator > iterator
non-const iterator type
Definition BaseTypes.h:829
auto operator[](const std::string &key)
non-const operator[]
Definition BaseTypes.h:872
size_t size() const
Get the size of the array.
Definition BaseTypes.h:847
T at(size_t index) const
Direct getter for a particular element.
Definition BaseTypes.h:856
auto operator[](const std::string &key) const
const operator[]
Definition BaseTypes.h:862
auto end() const
Returns a const iterator to the end of the dictionary.
Definition BaseTypes.h:944
auto begin() const
Returns a const iterator to the beginning of the dictionary.
Definition BaseTypes.h:941
auto find(const std::string &key) const
Finds an element by key and returns a const iterator.
Definition BaseTypes.h:923
U append(const std::string &key, Args &&... args)
Create a new element using the input key. (Available only for Base types)
Definition BaseTypes.h:895
void erase(const std::string &key)
Remove an element at a given key.
Definition BaseTypes.h:906
Dictionary(Base &parent, std::string_view key)
Creates a new Dictionary class as a child of a JSON node.
Definition BaseTypes.h:843
iter< const Dictionary, json::const_iterator > const_iterator
const iterator type
Definition BaseTypes.h:830
bool contains(const std::string &key) const
Returns true if the key exists in in the dictionary.
Definition BaseTypes.h:931
T value_type
The type for elements in this Array.
Definition BaseTypes.h:827
auto begin()
Returns an iterator to the beginning of the dictionary.
Definition BaseTypes.h:935
bool empty() const
Check if the array is empty.
Definition BaseTypes.h:850
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:884
Dictionary(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps an Dictionary class around an existing JSON node.
Definition BaseTypes.h:835
auto end()
Returns an iterator to the end of the dictionary.
Definition BaseTypes.h:938
auto find(const std::string &key)
Finds an element by key and returns an iterator.
Definition BaseTypes.h:914
void clear()
Clear all elements.
Definition BaseTypes.h:853
Represents an MNX document and provides methods for loading and saving.
Definition Document.h:87
Represents an MNX object, encapsulating property access.
Definition BaseTypes.h:426
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:431
MNX_OPTIONAL_CHILD(Object, _x)
Vendor-defined dictionary.
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:441
Allows access to a fundamental type (number, boolean, string) in a JSON node.
Definition BaseTypes.h:453
SimpleType(const std::shared_ptr< json > &root, json_pointer pointer)
Wraps a SimpleType class around an existing JSON object node.
Definition BaseTypes.h:462
bool operator==(const T &src) const
Equality comparison with value type.
Definition BaseTypes.h:481
T value_type
value type of this SimpleType
Definition BaseTypes.h:457
SimpleType & operator=(const T &src)
Allow assignment to underlying json reference.
Definition BaseTypes.h:474
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:420
json::json_pointer json_pointer
JSON pointer class for MNX.
Definition BaseTypes.h:198
constexpr int MNX_VERSION
The MNX version for files generated by the DOM.
Definition BaseTypes.h:195
nlohmann::ordered_json json
JSON class for MNX.
Definition BaseTypes.h:197
Supplies enum string mappings to nlohmann json's serializer.
Definition BaseTypes.h:763
static const std::unordered_map< E, std::string > enumToString()
maps enum values to strings
Definition BaseTypes.h:767
static const std::unordered_map< std::string, E > stringToEnum()
maps strings to enum values