Add visitor support to Value
Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
@@ -21,4 +21,4 @@ namespace AZ::Dom::Utils
|
||||
{
|
||||
return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor);
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Dom::Utils
|
||||
|
||||
@@ -744,4 +744,310 @@ namespace AZ::Dom
|
||||
{
|
||||
m_value = AZStd::monostate();
|
||||
}
|
||||
|
||||
Visitor::Result Value::Accept(Visitor& visitor, bool copyStrings) const
|
||||
{
|
||||
Visitor::Result result = AZ::Success();
|
||||
|
||||
AZStd::visit(
|
||||
[&](auto&& arg)
|
||||
{
|
||||
using Alternative = AZStd::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (AZStd::is_same_v<Alternative, AZStd::monostate>)
|
||||
{
|
||||
result = visitor.Null();
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, int64_t>)
|
||||
{
|
||||
result = visitor.Int64(arg);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, uint64_t>)
|
||||
{
|
||||
result = visitor.Uint64(arg);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, double>)
|
||||
{
|
||||
result = visitor.Double(arg);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, AZStd::string_view>)
|
||||
{
|
||||
result = visitor.String(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, AZStd::shared_ptr<AZStd::string>>)
|
||||
{
|
||||
result = visitor.String(*arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, ObjectPtr>)
|
||||
{
|
||||
result = visitor.StartObject();
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
const Object::ContainerType& object = GetObjectInternal();
|
||||
for (const Object::EntryType& entry : object)
|
||||
{
|
||||
result = entry.second.Accept(visitor, copyStrings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
result = visitor.EndObject(object.size());
|
||||
}
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, ArrayPtr>)
|
||||
{
|
||||
result = visitor.StartArray();
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
const Array::ContainerType& arrayContainer = GetArrayInternal();
|
||||
for (const Value& entry : arrayContainer)
|
||||
{
|
||||
result = entry.Accept(visitor, copyStrings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
result = visitor.EndArray(arrayContainer.size());
|
||||
}
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, Node>)
|
||||
{
|
||||
const Node& node = AZStd::get<Node>(m_value);
|
||||
result = visitor.StartNode(node.GetName());
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
const Object::ContainerType& object = GetObjectInternal();
|
||||
for (const Object::EntryType& entry : object)
|
||||
{
|
||||
result = entry.second.Accept(visitor, copyStrings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const Array::ContainerType& arrayContainer = GetArrayInternal();
|
||||
for (const Value& entry : arrayContainer)
|
||||
{
|
||||
result = entry.Accept(visitor, copyStrings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result = visitor.EndNode(object.size(), arrayContainer.size());
|
||||
}
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, AZStd::any>)
|
||||
{
|
||||
result = visitor.OpaqueValue(*arg);
|
||||
}
|
||||
},
|
||||
m_value);
|
||||
return result;
|
||||
}
|
||||
|
||||
ValueWriter::ValueWriter(Value& outputValue)
|
||||
: m_result(outputValue)
|
||||
{
|
||||
}
|
||||
|
||||
VisitorFlags ValueWriter::GetVisitorFlags() const
|
||||
{
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes;
|
||||
}
|
||||
|
||||
ValueWriter::ValueInfo::ValueInfo(Value& container)
|
||||
: m_container(container)
|
||||
{
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::Null()
|
||||
{
|
||||
CurrentValue().SetNull();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::Bool(bool value)
|
||||
{
|
||||
CurrentValue().SetBool(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::Int64(AZ::s64 value)
|
||||
{
|
||||
CurrentValue().SetInt(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::Uint64(AZ::u64 value)
|
||||
{
|
||||
CurrentValue().SetUint(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::Double(double value)
|
||||
{
|
||||
CurrentValue().SetDouble(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::String(AZStd::string_view value, Lifetime lifetime)
|
||||
{
|
||||
if (lifetime == Lifetime::Persistent)
|
||||
{
|
||||
CurrentValue().SetString(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentValue().CopyFromString(value);
|
||||
}
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::StartObject()
|
||||
{
|
||||
CurrentValue().SetObject();
|
||||
|
||||
m_entryStack.emplace(CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount)
|
||||
{
|
||||
const char* endMethodName;
|
||||
switch (containerType)
|
||||
{
|
||||
case Type::ObjectType:
|
||||
endMethodName = "EndObject";
|
||||
break;
|
||||
case Type::ArrayType:
|
||||
endMethodName = "EndArray";
|
||||
break;
|
||||
case Type::NodeType:
|
||||
endMethodName = "EndNode";
|
||||
break;
|
||||
default:
|
||||
AZ_Assert(false, "Invalid container type specified");
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "AZ::Dom::ValueWriter: EndContainer called with invalid container type");
|
||||
}
|
||||
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format("AZ::Dom::ValueWriter: %s called without a matching call", endMethodName));
|
||||
}
|
||||
|
||||
const ValueInfo& topEntry = m_entryStack.top();
|
||||
if (topEntry.m_container.GetType() != containerType)
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName));
|
||||
}
|
||||
|
||||
if (topEntry.m_attributeCount != attributeCount)
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format(
|
||||
"AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount,
|
||||
topEntry.m_attributeCount));
|
||||
}
|
||||
|
||||
if (topEntry.m_elementCount != elementCount)
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format(
|
||||
"AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount,
|
||||
topEntry.m_elementCount));
|
||||
}
|
||||
|
||||
m_entryStack.pop();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount)
|
||||
{
|
||||
return EndContainer(Type::ObjectType, attributeCount, 0);
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::Key(AZ::Name key)
|
||||
{
|
||||
AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object");
|
||||
AZ_Assert(!m_entryStack.top().m_container.IsArray(), "Attempted to push a key to an array");
|
||||
m_entryStack.top().m_key = key;
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
return Key(AZ::Name(key));
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::StartArray()
|
||||
{
|
||||
CurrentValue().SetArray();
|
||||
|
||||
m_entryStack.emplace(CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::EndArray(AZ::u64 elementCount)
|
||||
{
|
||||
return EndContainer(Type::ArrayType, 0, elementCount);
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::StartNode(AZ::Name name)
|
||||
{
|
||||
CurrentValue().SetNode(name);
|
||||
|
||||
m_entryStack.emplace(CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::EndNode(AZ::u64 attributeCount, AZ::u64 elementCount)
|
||||
{
|
||||
return EndContainer(Type::NodeType, attributeCount, elementCount);
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::FinishWrite()
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Value value;
|
||||
m_entryStack.top().m_value.Swap(value);
|
||||
ValueInfo& newEntry = m_entryStack.top();
|
||||
|
||||
if (!newEntry.m_key.IsEmpty())
|
||||
{
|
||||
newEntry.m_container.AddMember(newEntry.m_key, AZStd::move(value));
|
||||
newEntry.m_key = AZ::Name();
|
||||
++newEntry.m_attributeCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
newEntry.m_container.PushBack(AZStd::move(value));
|
||||
++newEntry.m_elementCount;
|
||||
}
|
||||
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Value& ValueWriter::CurrentValue()
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
return m_entryStack.top().m_value;
|
||||
}
|
||||
} // namespace AZ::Dom
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
|
||||
namespace AZ::Dom
|
||||
@@ -249,6 +250,9 @@ namespace AZ::Dom
|
||||
// null API...
|
||||
void SetNull();
|
||||
|
||||
// Visitor API
|
||||
Visitor::Result Accept(Visitor& visitor, bool copyStrings) const;
|
||||
|
||||
private:
|
||||
const Object::ContainerType& GetObjectInternal() const;
|
||||
Object::ContainerType& GetObjectInternal();
|
||||
@@ -286,4 +290,47 @@ namespace AZ::Dom
|
||||
|
||||
ValueType m_value;
|
||||
};
|
||||
} // namespace AZ::Dom
|
||||
|
||||
class ValueWriter : public Visitor
|
||||
{
|
||||
public:
|
||||
ValueWriter(Value& outputValue);
|
||||
|
||||
VisitorFlags GetVisitorFlags() const override;
|
||||
Result Null() override;
|
||||
Result Bool(bool value) override;
|
||||
Result Int64(AZ::s64 value) override;
|
||||
Result Uint64(AZ::u64 value) override;
|
||||
Result Double(double value) override;
|
||||
|
||||
Result String(AZStd::string_view value, Lifetime lifetime) override;
|
||||
Result StartObject() override;
|
||||
Result EndObject(AZ::u64 attributeCount) override;
|
||||
Result Key(AZ::Name key) override;
|
||||
Result RawKey(AZStd::string_view key, Lifetime lifetime) override;
|
||||
Result StartArray() override;
|
||||
Result EndArray(AZ::u64 elementCount) override;
|
||||
Result StartNode(AZ::Name name) override;
|
||||
Result RawStartNode(AZStd::string_view name, Lifetime lifetime) override;
|
||||
Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) override;
|
||||
|
||||
private:
|
||||
Result FinishWrite();
|
||||
Value& CurrentValue();
|
||||
Visitor::Result EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount);
|
||||
|
||||
struct ValueInfo
|
||||
{
|
||||
ValueInfo(Value& container);
|
||||
|
||||
KeyType m_key;
|
||||
Value m_value;
|
||||
Value& m_container;
|
||||
AZ::u64 m_attributeCount = 0;
|
||||
AZ::u64 m_elementCount = 0;
|
||||
};
|
||||
|
||||
Value& m_result;
|
||||
AZStd::stack<ValueInfo> m_entryStack;
|
||||
};
|
||||
} // namespace AZ::Dom
|
||||
|
||||
Reference in New Issue
Block a user