Add visitor support to Value

Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
Nicholas Van Sickle
2021-12-08 22:22:18 -08:00
parent f36255b22a
commit fada7ace23
3 changed files with 355 additions and 2 deletions
@@ -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
+48 -1
View File
@@ -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