Initial value implementation

Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
Nicholas Van Sickle
2021-12-08 13:57:24 -08:00
parent 6cf805256b
commit f36255b22a
5 changed files with 1040 additions and 0 deletions
@@ -0,0 +1,747 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/DOM/DomValue.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace AZ::Dom
{
template<class T>
AZStd::shared_ptr<T>& CheckCopyOnWrite(AZStd::shared_ptr<T>& refCountedPointer)
{
if (refCountedPointer.use_count() > 1)
{
AZStd::shared_ptr<T> newPointer = AZStd::make_shared<T>();
*newPointer = *refCountedPointer;
refCountedPointer = AZStd::move(newPointer);
}
return refCountedPointer;
}
AZ::Name Node::GetName() const
{
return m_name;
}
void Node::SetName(AZ::Name name)
{
m_name = name;
}
ObjectPtr Node::GetMutableProperties()
{
CheckCopyOnWrite(m_object);
return m_object;
}
ConstObjectPtr Node::GetProperties() const
{
return m_object;
}
ArrayPtr Node::GetMutableChildren()
{
CheckCopyOnWrite(m_array);
return m_array;
}
ConstArrayPtr Node::GetChildren() const
{
return m_array;
}
bool Node::operator==(const Node& rhs) const
{
return m_name == rhs.m_name && m_array == rhs.m_array && m_object == rhs.m_object;
}
Value::Value()
{
}
Value::Value(const Value& value)
: m_value(value.m_value)
{
}
Value::Value(Value&& value) noexcept
: m_value(value.m_value)
{
}
Value::Value(AZStd::string_view string, bool copy)
{
if (copy)
{
CopyFromString(string);
}
else
{
SetString(string);
}
}
Value::Value(AZStd::any* value)
: m_value(value)
{
}
Value Value::FromOpaqueValue(AZStd::any& value)
{
return Value(&value);
}
Value::Value(int64_t value)
: m_value(value)
{
}
Value::Value(uint64_t value)
: m_value(value)
{
}
Value::Value(double value)
: m_value(value)
{
}
Value::Value(bool value)
: m_value(value)
{
}
Value& Value::operator=(const Value& other)
{
m_value = other.m_value;
return *this;
}
Value& Value::operator=(Value&& other) noexcept
{
m_value = other.m_value;
return *this;
}
bool Value::operator==(const Value& rhs) const
{
if (IsString() && rhs.IsString())
{
return GetString() == rhs.GetString();
}
else if (IsNumber() && rhs.IsNumber())
{
if (IsInt())
{
return GetInt() == rhs.GetInt();
}
else if (IsUint())
{
return GetUint() == rhs.GetUint();
}
else
{
return GetDouble() == rhs.GetDouble();
}
}
else
{
return m_value == rhs.m_value;
}
}
bool Value::operator!=(const Value& rhs) const
{
return !operator==(rhs);
}
void Value::Swap(Value& other) noexcept
{
AZStd::swap(m_value, other.m_value);
}
Type Dom::Value::GetType() const
{
switch (m_value.index())
{
case 0: // AZStd::monostate
return Type::NullType;
case 1: // int64_t
case 2: // uint64_t
case 3: // double
return Type::NumberType;
case 4: // bool
return AZStd::get<bool>(m_value) ? Type::TrueType : Type::FalseType;
case 5: // AZStd::string_view
case 6: // AZStd::shared_ptr<AZStd::string>
return Type::StringType;
case 7: // ObjectPtr
return Type::ObjectType;
case 8: // ArrayPtr
return Type::ArrayType;
case 9: // Node
return Type::NodeType;
case 10: // AZStd::any*
return Type::OpaqueType;
}
AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type");
return Type::NullType;
}
bool Value::IsNull() const
{
return GetType() == Type::NullType;
}
bool Value::IsFalse() const
{
return GetType() == Type::FalseType;
}
bool Value::IsTrue() const
{
return GetType() == Type::TrueType;
}
bool Value::IsBool() const
{
return AZStd::holds_alternative<bool>(m_value);
}
bool Value::IsNode() const
{
return GetType() == Type::NodeType;
}
bool Value::IsObject() const
{
return GetType() == Type::ObjectType;
}
bool Value::IsArray() const
{
return GetType() == Type::ArrayType;
}
bool Value::IsOpaqueValue() const
{
return GetType() == Type::OpaqueType;
}
bool Value::IsNumber() const
{
return GetType() == Type::NumberType;
}
bool Value::IsInt() const
{
return AZStd::holds_alternative<int64_t>(m_value);
}
bool Value::IsUint() const
{
return AZStd::holds_alternative<uint64_t>(m_value);
}
bool Value::IsDouble() const
{
return AZStd::holds_alternative<double>(m_value);
}
bool Value::IsString() const
{
return GetType() == Type::StringType;
}
Value& Value::SetObject()
{
m_value = AZStd::make_shared<Object>();
return *this;
}
const Object::ContainerType& Value::GetObjectInternal() const
{
const Type type = GetType();
AZ_Assert(
type == Type::ObjectType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an object from a non-object value");
if (type == Type::ObjectType)
{
return AZStd::get<ObjectPtr>(m_value)->m_values;
}
else
{
return AZStd::get<Node>(m_value).GetProperties()->m_values;
}
}
Object::ContainerType& Value::GetObjectInternal()
{
const Type type = GetType();
AZ_Assert(
type == Type::ObjectType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an object from a non-object value");
if (type == Type::ObjectType)
{
return CheckCopyOnWrite(AZStd::get<ObjectPtr>(m_value))->m_values;
}
else
{
return AZStd::get<Node>(m_value).GetMutableProperties()->m_values;
}
}
const Array::ContainerType& Value::GetArrayInternal() const
{
const Type type = GetType();
AZ_Assert(
type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a non-array value");
if (type == Type::ObjectType)
{
return AZStd::get<ArrayPtr>(m_value)->m_values;
}
else
{
return AZStd::get<Node>(m_value).GetChildren()->m_values;
}
}
Array::ContainerType& Value::GetArrayInternal()
{
const Type type = GetType();
AZ_Assert(
type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a non-array value");
if (type == Type::ObjectType)
{
return CheckCopyOnWrite(AZStd::get<ArrayPtr>(m_value))->m_values;
}
else
{
return AZStd::get<Node>(m_value).GetMutableChildren()->m_values;
}
}
size_t Value::MemberCount() const
{
return GetObjectInternal().size();
}
size_t Value::MemberCapacity() const
{
return GetObjectInternal().size();
}
bool Value::ObjectEmpty() const
{
return GetObjectInternal().empty();
}
Value& Value::operator[](KeyType name)
{
return FindMember(name)->second;
}
const Value& Value::operator[](KeyType name) const
{
return FindMember(name)->second;
}
Value& Value::operator[](AZStd::string_view name)
{
return operator[](AZ::Name(name));
}
const Value& Value::operator[](AZStd::string_view name) const
{
return operator[](AZ::Name(name));
}
Object::ConstIterator Value::MemberBegin() const
{
return GetObjectInternal().begin();
}
Object::ConstIterator Value::MemberEnd() const
{
return GetObjectInternal().end();
}
Object::Iterator Value::MemberBegin()
{
return GetObjectInternal().begin();
}
Object::Iterator Value::MemberEnd()
{
return GetObjectInternal().end();
}
Object::ConstIterator Value::FindMember(KeyType name) const
{
const Object::ContainerType& object = GetObjectInternal();
return AZStd::find_if(
object.begin(), object.end(),
[&name](const Object::EntryType& entry)
{
return entry.first == name;
});
}
Object::ConstIterator Value::FindMember(AZStd::string_view name) const
{
return FindMember(AZ::Name(name));
}
Object::Iterator Value::FindMember(KeyType name)
{
Object::ContainerType& object = GetObjectInternal();
return AZStd::find_if(
object.begin(), object.end(),
[&name](const Object::EntryType& entry)
{
return entry.first == name;
});
}
Object::Iterator Value::FindMember(AZStd::string_view name)
{
return FindMember(AZ::Name(name));
}
Value& Value::MemberReserve(size_t newCapacity)
{
GetObjectInternal().reserve(newCapacity);
return *this;
}
bool Value::HasMember(KeyType name) const
{
return FindMember(name) != GetObjectInternal().end();
}
bool Value::HasMember(AZStd::string_view name) const
{
return HasMember(AZ::Name(name));
}
Value& Value::AddMember(KeyType name, const Value& value)
{
Object::ContainerType& object = GetObjectInternal();
if (auto memberIt = FindMember(name); memberIt != object.end())
{
memberIt->second = value;
}
else
{
object.emplace_back(name, value);
}
return *this;
}
Value& Value::AddMember(AZStd::string_view name, const Value& value)
{
return AddMember(AZ::Name(name), value);
}
Value& Value::AddMember(AZ::Name name, Value&& value)
{
Object::ContainerType& object = GetObjectInternal();
if (auto memberIt = FindMember(name); memberIt != object.end())
{
memberIt->second = value;
}
else
{
object.emplace_back(name, value);
}
return *this;
}
Value& Value::AddMember(AZStd::string_view name, Value&& value)
{
return AddMember(AZ::Name(name), value);
}
void Value::RemoveAllMembers()
{
GetObjectInternal().clear();
}
void Value::RemoveMember(KeyType name)
{
Object::ContainerType& object = GetObjectInternal();
object.erase(AZStd::remove_if(
object.begin(), object.end(),
[&name](const Object::EntryType& entry)
{
return entry.first == name;
}));
}
void Value::RemoveMember(AZStd::string_view name)
{
RemoveMember(AZ::Name(name));
}
Object::Iterator Value::RemoveMember(Object::Iterator pos)
{
Object::ContainerType& object = GetObjectInternal();
Object::Iterator nextIndex = object.end();
auto lastEntry = object.end() - 1;
if (pos != lastEntry)
{
AZStd::swap(*pos, *lastEntry);
nextIndex = pos;
}
object.resize(object.size() - 1);
return nextIndex;
}
Object::Iterator Value::EraseMember(Object::ConstIterator pos)
{
return GetObjectInternal().erase(pos);
}
Object::Iterator Value::EraseMember(Object::ConstIterator first, Object::ConstIterator last)
{
return GetObjectInternal().erase(first, last);
}
Object::Iterator Value::EraseMember(KeyType name)
{
return GetObjectInternal().erase(FindMember(name));
}
Object::Iterator Value::EraseMember(AZStd::string_view name)
{
return EraseMember(AZ::Name(name));
}
Object::ContainerType& Value::GetObject()
{
return GetObjectInternal();
}
const Object::ContainerType& Value::GetObject() const
{
return GetObjectInternal();
}
Value& Value::SetArray()
{
m_value = AZStd::make_shared<Array>();
return *this;
}
size_t Value::Size() const
{
return GetArrayInternal().size();
}
size_t Value::Capacity() const
{
return GetArrayInternal().capacity();
}
bool Value::Empty() const
{
return GetArrayInternal().empty();
}
void Value::Clear()
{
GetArrayInternal().clear();
}
Value& Value::operator[](size_t index)
{
return GetArrayInternal()[index];
}
const Value& Value::operator[](size_t index) const
{
return GetArrayInternal()[index];
}
Array::ConstIterator Value::Begin() const
{
return GetArrayInternal().begin();
}
Array::ConstIterator Value::End() const
{
return GetArrayInternal().end();
}
Array::Iterator Value::Begin()
{
return GetArrayInternal().begin();
}
Array::Iterator Value::End()
{
return GetArrayInternal().end();
}
Value& Value::Reserve(size_t newCapacity)
{
GetArrayInternal().reserve(newCapacity);
return *this;
}
Value& Value::PushBack(Value value)
{
GetArrayInternal().push_back(AZStd::move(value));
return *this;
}
Value& Value::PopBack()
{
GetArrayInternal().pop_back();
return *this;
}
Array::Iterator Value::Erase(Array::ConstIterator pos)
{
return GetArrayInternal().erase(pos);
}
Array::Iterator Value::Erase(Array::ConstIterator first, Array::ConstIterator last)
{
return GetArrayInternal().erase(first, last);
}
Array::ContainerType& Value::GetArray()
{
return GetArrayInternal();
}
const Array::ContainerType& Value::GetArray() const
{
return GetArrayInternal();
}
int64_t Value::GetInt() const
{
switch (m_value.index())
{
case 1: // int64_t
return AZStd::get<int64_t>(m_value);
case 2: // uint64_t
return aznumeric_cast<int64_t>(AZStd::get<uint64_t>(m_value));
case 3: // double
return aznumeric_cast<int64_t>(AZStd::get<double>(m_value));
}
AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type");
return {};
}
void Value::SetInt(int64_t value)
{
m_value = value;
}
uint64_t Value::GetUint() const
{
switch (m_value.index())
{
case 1: // int64_t
return aznumeric_cast<uint64_t>(AZStd::get<int64_t>(m_value));
case 2: // uint64_t
return AZStd::get<uint64_t>(m_value);
case 3: // double
return aznumeric_cast<uint64_t>(AZStd::get<double>(m_value));
}
AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type");
return {};
}
void Value::SetUint(uint64_t value)
{
m_value = value;
}
bool Value::GetBool() const
{
if (IsBool())
{
return AZStd::get<bool>(m_value);
}
AZ_Assert(false, "AZ::Dom::Value: Called GetBool on a non-bool type");
return {};
}
void Value::SetBool(bool value)
{
m_value = value;
}
double Value::GetDouble() const
{
switch (m_value.index())
{
case 1: // int64_t
return aznumeric_cast<double>(AZStd::get<int64_t>(m_value));
case 2: // uint64_t
return aznumeric_cast<double>(AZStd::get<uint64_t>(m_value));
case 3: // double
return AZStd::get<double>(m_value);
}
AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type");
return {};
}
void Value::SetDouble(double value)
{
m_value = value;
}
AZStd::string_view Value::GetString() const
{
switch (m_value.index())
{
case 5: // AZStd::string_view
return AZStd::get<AZStd::string_view>(m_value);
case 6: // AZStd::shared_ptr<AZStd::string>
return *AZStd::get<AZStd::shared_ptr<AZStd::string>>(m_value);
}
AZ_Assert(false, "AZ::Dom::Value: Called GetString on a non-string type");
return {};
}
size_t Value::GetStringLength() const
{
return GetString().size();
}
void Value::SetString(AZStd::string_view value)
{
m_value = value;
}
void Value::CopyFromString(AZStd::string_view value)
{
m_value = AZStd::make_shared<AZStd::string>(value);
}
AZStd::any& Value::GetOpaqueValue() const
{
return *AZStd::get<AZStd::any*>(m_value);
}
void Value::SetOpaqueValue(AZStd::any& value)
{
m_value = &value;
}
void Value::SetNull()
{
m_value = AZStd::monostate();
}
} // namespace AZ::Dom
+289
View File
@@ -0,0 +1,289 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/DOM/DomVisitor.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
namespace AZ::Dom
{
using KeyType = AZ::Name;
enum class Type
{
NullType = 0,
FalseType = 1,
TrueType = 2,
ObjectType = 3,
ArrayType = 4,
StringType = 5,
NumberType = 6,
NodeType = 7,
OpaqueType = 8,
};
class Value;
class Array
{
public:
using ContainerType = AZStd::vector<Value>;
using Iterator = ContainerType::iterator;
using ConstIterator = ContainerType::const_iterator;
private:
ContainerType m_values;
friend class Value;
};
using ArrayPtr = AZStd::shared_ptr<Array>;
using ConstArrayPtr = AZStd::shared_ptr<const Array>;
class Object
{
public:
using EntryType = AZStd::pair<KeyType, Value>;
using ContainerType = AZStd::vector<EntryType>;
using Iterator = ContainerType::iterator;
using ConstIterator = ContainerType::const_iterator;
private:
ContainerType m_values;
friend class Value;
};
using ObjectPtr = AZStd::shared_ptr<Object>;
using ConstObjectPtr = AZStd::shared_ptr<const Object>;
class Node
{
public:
AZ::Name GetName() const;
void SetName(AZ::Name name);
ObjectPtr GetMutableProperties();
ConstObjectPtr GetProperties() const;
ArrayPtr GetMutableChildren();
ConstArrayPtr GetChildren() const;
bool operator==(const Node& rhs) const;
private:
AZ::Name m_name;
ArrayPtr m_array;
ObjectPtr m_object;
friend class Value;
};
class Value
{
public:
// Constructors...
Value();
Value(const Value&);
Value(Value&&) noexcept;
Value(AZStd::string_view string, bool copy);
explicit Value(int64_t value);
explicit Value(uint64_t value);
explicit Value(double value);
explicit Value(bool value);
static Value FromOpaqueValue(AZStd::any& value);
// Equality / comparison / swap...
Value& operator=(const Value&);
Value& operator=(Value&&) noexcept;
bool operator==(const Value& rhs) const;
bool operator!=(const Value& rhs) const;
void Swap(Value& other) noexcept;
// Type info...
Type GetType() const;
bool IsNull() const;
bool IsFalse() const;
bool IsTrue() const;
bool IsBool() const;
bool IsNode() const;
bool IsObject() const;
bool IsArray() const;
bool IsOpaqueValue() const;
bool IsNumber() const;
bool IsInt() const;
bool IsUint() const;
bool IsDouble() const;
bool IsString() const;
// Object API (also used by Node)...
Value& SetObject();
size_t MemberCount() const;
size_t MemberCapacity() const;
bool ObjectEmpty() const;
Value& operator[](KeyType name);
const Value& operator[](KeyType name) const;
Value& operator[](AZStd::string_view name);
const Value& operator[](AZStd::string_view name) const;
Object::ConstIterator MemberBegin() const;
Object::ConstIterator MemberEnd() const;
Object::Iterator MemberBegin();
Object::Iterator MemberEnd();
Object::ConstIterator FindMember(KeyType name) const;
Object::ConstIterator FindMember(AZStd::string_view name) const;
Object::Iterator FindMember(KeyType name);
Object::Iterator FindMember(AZStd::string_view name);
Value& MemberReserve(size_t newCapacity);
bool HasMember(KeyType name) const;
bool HasMember(AZStd::string_view name) const;
Value& AddMember(KeyType name, const Value& value);
Value& AddMember(AZStd::string_view name, const Value& value);
Value& AddMember(KeyType name, Value&& value);
Value& AddMember(AZStd::string_view name, Value&& value);
void RemoveAllMembers();
void RemoveMember(KeyType name);
void RemoveMember(AZStd::string_view name);
Object::Iterator RemoveMember(Object::Iterator pos);
Object::Iterator EraseMember(Object::ConstIterator pos);
Object::Iterator EraseMember(Object::ConstIterator first, Object::ConstIterator last);
Object::Iterator EraseMember(KeyType name);
Object::Iterator EraseMember(AZStd::string_view name);
Object::ContainerType& GetObject();
const Object::ContainerType& GetObject() const;
// Array API (also used by Node)...
Value& SetArray();
size_t Size() const;
size_t Capacity() const;
bool Empty() const;
void Clear();
Value& operator[](size_t index);
const Value& operator[](size_t index) const;
Array::ConstIterator Begin() const;
Array::ConstIterator End() const;
Array::Iterator Begin();
Array::Iterator End();
Value& Reserve(size_t newCapacity);
Value& PushBack(Value value);
Value& PopBack();
Array::Iterator Erase(Array::ConstIterator pos);
Array::Iterator Erase(Array::ConstIterator first, Array::ConstIterator last);
Array::ContainerType& GetArray();
const Array::ContainerType& GetArray() const;
// Node API (supports both object + array API, plus a dedicated NodeName)...
// bool CanConvertToNodeFromObject() const;
// Value& ConvertToNodeFromObject();
// Value& ConvertToObjectFromNode();
void SetNode(AZ::Name name);
void SetNode(AZStd::string_view name);
AZ::Name GetNodeName() const;
void SetNodeName(AZ::Name name);
void SetNodeName(AZStd::string_view name);
//! Convenience method, sets the first non-node element of a Node.
void SetNodeValue(Value value);
//! Convenience method, gets the first non-node element of a Node.
Value GetNodeValue() const;
// int API...
int64_t GetInt() const;
void SetInt(int64_t);
// uint API...
uint64_t GetUint() const;
void SetUint(uint64_t);
// bool API...
bool GetBool() const;
void SetBool(bool);
// double API...
double GetDouble() const;
void SetDouble(double);
// string API...
AZStd::string_view GetString() const;
size_t GetStringLength() const;
void SetString(AZStd::string_view);
void CopyFromString(AZStd::string_view);
// opaque type API...
AZStd::any& GetOpaqueValue() const;
//! This sets this Value to represent a value of an type that the DOM has
//! no formal knowledge of. Where possible, it should be preferred to
//! serialize an opaque type into a DOM value instead, as serializers
//! and other systems will have no means of dealing with fully arbitrary
//! values.
void SetOpaqueValue(AZStd::any&);
// null API...
void SetNull();
private:
const Object::ContainerType& GetObjectInternal() const;
Object::ContainerType& GetObjectInternal();
const Array::ContainerType& GetArrayInternal() const;
Array::ContainerType& GetArrayInternal();
explicit Value(AZStd::any* opaqueValue);
// If using the the copy on write model, anything stored internally as a shared_ptr will
// detach and copy when doing a mutating operation if use_count() > 1.
// This internal storage will not have a 1:1 mapping to the public Type, as there may be
// multiple storage options (e.g. strings being stored as non-owning string_view or
// owning shared_ptr<string>)
using ValueType = AZStd::variant<
// NullType
AZStd::monostate,
// NumberType
int64_t,
uint64_t,
double,
// FalseType & TrueType
bool,
// StringType
AZStd::string_view,
AZStd::shared_ptr<AZStd::string>,
// ObjectType
ObjectPtr,
// ArrayType
ArrayPtr,
// NodeType
Node,
// OpaqueType
AZStd::any*>;
ValueType m_value;
};
} // namespace AZ::Dom
@@ -127,8 +127,12 @@ set(FILES
Debug/TraceReflection.h
DOM/DomBackend.cpp
DOM/DomBackend.h
DOM/DomDocument.cpp
DOM/DomDocument.h
DOM/DomUtils.cpp
DOM/DomUtils.h
DOM/DomValue.cpp
DOM/DomValue.h
DOM/DomVisitor.cpp
DOM/DomVisitor.h
DOM/Backends/JSON/JsonBackend.h