25#include <unordered_map>
31#include "nlohmann/json.hpp"
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); \
49 return ref()[#NAME].get<TYPE>(); \
51 void set_##NAME(const TYPE& value) { ref()[#NAME] = value; } \
52 static_assert(true, "")
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, "")
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; \
92 [[nodiscard]] TYPE NAME##_or(const TYPE& defaultVal) const { \
93 return ref().contains(KEY) ? ref()[KEY].get<TYPE>() : defaultVal; \
95 void set_##NAME(const TYPE& value) { ref()[KEY] = value; } \
96 void clear_##NAME() { ref().erase(KEY); } \
97 static_assert(true, "")
112 #define MNX_OPTIONAL_PROPERTY(TYPE, NAME) MNX_OPTIONAL_NAMED_PROPERTY(TYPE, NAME, #NAME)
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; \
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); \
139 static_assert(true, "")
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)...)); \
158 static_assert(true, "")
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)...)); \
179 void clear_##NAME() { ref().erase(#NAME); } \
180 static_assert(true, "")
182#define MNX_ASSERT_IF(TEST) \
197using json = nlohmann::ordered_json;
202template <
typename T>
class Array;
204namespace validation {
205class SemanticValidator;
214 virtual ~Base() =
default;
217 Base(
const Base& src) : m_root(src.m_root), m_pointer(src.m_pointer)
222 m_pointer(std::move(src.m_pointer))
229 if (m_root != src.m_root) {
230 throw std::logic_error(
"Assignment from a different JSON document is not allowed.");
232 m_pointer = src.m_pointer;
241 if (m_root != src.m_root) {
242 throw std::logic_error(
"Assignment from a different JSON document is not allowed.");
244 m_pointer = std::move(src.m_pointer);
251 [[nodiscard]] std::string
dump(
int indents = -1)
const
253 return ref().dump(indents);
259 template <
typename T>
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());
270 template <
typename T>
285 [[nodiscard]]
json&
ref()
const {
return resolve_pointer(); }
291 [[nodiscard]]
json&
ref() {
return resolve_pointer(); }
294 [[nodiscard]]
const std::shared_ptr<json>&
root()
const {
return m_root; }
311 : m_root(
parent.m_root), m_pointer(
parent.m_pointer / std::string(key))
313 (*m_root)[m_pointer] = std::move(jsonRef);
323 template <
typename T>
324 [[nodiscard]] T
getChild(std::string_view key)
const
326 static_assert(std::is_base_of_v<Base, T>,
"template type must be derived from Base");
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));
333 return T(m_root, childPointer);
343 template <
typename T>
346 static_assert(std::is_base_of_v<Base, T>,
"template type must be derived from Base");
348 json_pointer childPointer = m_pointer / std::string(key);
349 (*m_root)[childPointer] = value.ref();
350 return T(m_root, childPointer);
360 template <
typename T>
363 static_assert(std::is_base_of_v<Base, T>,
"template type must be derived from Base");
365 json_pointer childPointer = m_pointer / std::string(key);
366 if (!checkKeyIsValid<T>(childPointer)) {
370 return T(m_root, childPointer);
381 template <
typename T>
384 if (!(*m_root).contains(
pointer)) {
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());
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());
408 [[nodiscard]]
json& resolve_pointer()
const
410 return (*m_root).at(m_pointer);
413 const std::shared_ptr<json> m_root;
416 friend class validation::SemanticValidator;
433 if (!
ref().is_object()) {
434 throw std::invalid_argument(
"mnx::Object must wrap a JSON object.");
451template <
typename T, std::enable_if_t<!std::is_base_of_v<Base, T>,
int> = 0>
454 static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>,
"This template is for simple JSON classes");
469 return ref().template get<T>();
483 return src ==
ref().template get<T>();
487class ArrayElementObject;
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.");
498 template<
typename ArrayType>
503 mutable size_t m_idx;
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; }
524 if (!
ref().is_array()) {
525 throw std::invalid_argument(
"mnx::Array must wrap a JSON array.");
536 [[nodiscard]]
size_t size()
const {
return ref().size(); }
539 [[nodiscard]]
bool empty()
const {
return ref().empty(); }
545 [[nodiscard]] T
at(
size_t index)
const
554 if constexpr (std::is_base_of_v<Base, T>) {
555 return getChild<T>(std::to_string(index));
557 return getChild<SimpleType<T>>(std::to_string(index));
565 if constexpr (std::is_base_of_v<Base, T>) {
566 return getChild<T>(std::to_string(index));
568 return getChild<SimpleType<T>>(std::to_string(index));
573 template <
typename U = T>
574 std::enable_if_t<!std::is_base_of_v<Base, U>,
void>
577 ref().push_back(value);
584 template <
typename U = T,
typename... Args,
585 std::enable_if_t<std::is_base_of_v<Base, U>,
int> = 0>
588 if constexpr (std::is_base_of_v<Object, U>) {
589 ref().push_back(json::object());
591 ref().push_back(json::array());
593 return U(*
this, std::to_string(
ref().
size() - 1), std::forward<Args>(args)...);
622 throw std::out_of_range(
"Index out of range");
638 return std::stoul(
pointer().back());
646 template <
typename ContainerType>
649 return parent<Array<ArrayElementObject>>().parent<ContainerType>();
661 using ArrayElementObject::ArrayElementObject;
666 template <
typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>,
int> = 0>
667 [[nodiscard]] T
get()
const
669 return getTypedObject<T>();
675 template <
typename T, std::enable_if_t<std::is_base_of_v<ContentObject, T>,
int> = 0>
676 [[nodiscard]] T getTypedObject()
const
678 if (type() != T::ContentTypeValue) {
679 throw std::invalid_argument(
"Type mismatch: expected " + std::string(T::ContentTypeValue) +
685 friend class ContentArray;
719 using BaseArray::BaseArray;
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
730 template <
typename T,
typename... Args,
731 std::enable_if_t<std::is_base_of_v<ContentObject, T>,
int> = 0>
734 auto result = BaseArray::append<T>(std::forward<Args>(args)...);
736 result.set_type(std::string(T::ContentTypeValue));
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
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());
753 return T(
root(),
pointer() / std::to_string(index));
761template <
typename E,
typename = std::enable_if_t<std::is_enum_v<E>>>
769 static const std::unordered_map<E, std::string> reverseMap = []() {
770 std::unordered_map<E, std::string> result;
772 result.emplace(element.second, element.first);
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.");
790 template <
typename DictionaryType,
typename IteratorType>
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;
800 DictionaryType* m_ptr;
802 mutable std::unique_ptr<value_type> m_pair;
804 void update_pair()
const {
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()));
812 iter(DictionaryType* ptr, IteratorType it) : m_ptr(ptr), m_it(it)
815 [[nodiscard]] reference operator*()
const {
return *m_pair.get(); }
816 [[nodiscard]] pointer operator->()
const {
return m_pair.get(); }
818 iter& operator++() { ++m_it; update_pair();
return *
this; }
819 iter operator++(
int) { iter tmp = *
this; ++(*this);
return tmp; }
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; }
847 [[nodiscard]]
size_t size()
const {
return ref().size(); }
850 [[nodiscard]]
bool empty()
const {
return ref().empty(); }
856 [[nodiscard]] T
at(
size_t index)
const
864 if constexpr (std::is_base_of_v<Base, T>) {
865 return getChild<T>(key);
867 return getChild<SimpleType<T>>(key);
874 if constexpr (std::is_base_of_v<Base, T>) {
875 return getChild<T>(key);
877 return getChild<SimpleType<T>>(key);
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)
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)
897 if constexpr (std::is_base_of_v<Object, U>) {
898 ref()[key] = json::object();
900 ref()[key] = json::array();
902 return U(*
this, key, std::forward<Args>(args)...);
914 [[nodiscard]]
auto find(
const std::string& key)
916 auto it =
ref().find(key);
923 [[nodiscard]]
auto find(
const std::string& key)
const
925 auto it =
ref().find(key);
931 [[nodiscard]]
bool contains(
const std::string& key)
const
949#ifndef DOXYGEN_SHOULD_IGNORE_THIS
956template<
typename EnumType>
957struct adl_serializer<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
959 template<
typename BasicJsonType>
960 static EnumType from_json(
const BasicJsonType& j)
964 auto it = map.find(j.get<std::string>());
965 if (it != map.end()) {
972 template<
typename BasicJsonType>
973 static void to_json(BasicJsonType& j,
const EnumType& value)
976 auto it = map.find(value);
977 if (it == map.end()) {
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)
995 auto it = map.find(j.template get<std::string>());
996 if (it != map.end()) {
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
1009 auto it = map.find(value);
1010 if (it != map.end()) {
1014 j = BasicJsonType();
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