Add Generic DOM visitor interface (#4852)
* Add Generic DOM visitor interface Just the visitor interface from the [Generic DOM RFC](https://github.com/o3de/sig-content/blob/main/rfcs/rfc-10-generic-dom.md) with a few hardening changes so that we can align on it early: - Clarified Lifetimes with an enum, extended it to cover the by-ref opaque values as well - Added an explicit error type so that serializers can provide logging friendly rejections - Did a first pass on documentation - Added Visitor capabilities introspection and support for raw strings
This commit is contained in:
committed by
GitHub
parent
97920feaf1
commit
c5c043ecc5
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomVisitor.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
const char* VisitorError::CodeToString(VisitorErrorCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case VisitorErrorCode::UnsupportedOperation:
|
||||
return "operation not supported";
|
||||
case VisitorErrorCode::InvalidData:
|
||||
return "invalid data specified";
|
||||
case VisitorErrorCode::InternalError:
|
||||
return "internal error";
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
VisitorError::VisitorError(VisitorErrorCode code)
|
||||
: m_code(code)
|
||||
{
|
||||
}
|
||||
|
||||
VisitorError::VisitorError(VisitorErrorCode code, AZStd::string additionalInfo)
|
||||
: m_code(code)
|
||||
, m_additionalInfo(AZStd::move(additionalInfo))
|
||||
{
|
||||
}
|
||||
|
||||
VisitorErrorCode VisitorError::GetCode() const
|
||||
{
|
||||
return m_code;
|
||||
}
|
||||
|
||||
const AZStd::string& VisitorError::GetAdditionalInfo() const
|
||||
{
|
||||
return m_additionalInfo;
|
||||
}
|
||||
|
||||
AZStd::string VisitorError::FormatVisitorErrorMessage() const
|
||||
{
|
||||
if (m_additionalInfo.empty())
|
||||
{
|
||||
return AZStd::string::format("VisitorError: %s.", CodeToString(m_code));
|
||||
}
|
||||
return AZStd::string::format("VisitorError: %s. %s.", CodeToString(m_code), m_additionalInfo.c_str());
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code)
|
||||
{
|
||||
return AZ::Failure(VisitorError(code));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo)
|
||||
{
|
||||
return AZ::Failure(VisitorError(code, AZStd::move(additionalInfo)));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorFailure(VisitorError error)
|
||||
{
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorSuccess()
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Null()
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Bool([[maybe_unused]] bool value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Int64([[maybe_unused]] AZ::s64 value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Uint64([[maybe_unused]] AZ::u64 value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Double([[maybe_unused]] double value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::String([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
if (!SupportsOpaqueValues())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Opaque values are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::RawValue([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
if (!SupportsRawValues())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw values are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::StartObject()
|
||||
{
|
||||
if (!SupportsObjects())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::EndObject([[maybe_unused]] AZ::u64 attributeCount)
|
||||
{
|
||||
if (!SupportsObjects())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Key([[maybe_unused]] AZ::Name key)
|
||||
{
|
||||
if (!SupportsObjects() && !SupportsNodes())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Keys are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
if (!SupportsRawKeys())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw keys are not supported by this visitor");
|
||||
}
|
||||
return Key(AZ::Name(key));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::StartArray()
|
||||
{
|
||||
if (!SupportsArrays())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::EndArray([[maybe_unused]] AZ::u64 elementCount)
|
||||
{
|
||||
if (!SupportsArrays())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::StartNode([[maybe_unused]] AZ::Name name)
|
||||
{
|
||||
if (!SupportsNodes())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::RawStartNode(AZStd::string_view name, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
return StartNode(AZ::Name(name));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::EndNode([[maybe_unused]] AZ::u64 attributeCount, [[maybe_unused]] AZ::u64 elementCount)
|
||||
{
|
||||
if (!SupportsNodes())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
VisitorFlags Visitor::GetVisitorFlags() const
|
||||
{
|
||||
// By default support raw keys (promoting them to AZ::Name) and support Array / Object / Node
|
||||
// We leave Opaque type support and Raw Values to more specialized, implementation-specific cases
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsRawValues() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsRawValues) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsRawKeys() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsRawKeys) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsObjects() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsObjects) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsArrays() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsArrays) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsNodes() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsNodes) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsOpaqueValues() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsOpaqueValues) != VisitorFlags::Null;
|
||||
}
|
||||
} // namespace AZ::DOM
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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/Name/Name.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/any.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
//
|
||||
// Lifetime enum
|
||||
//
|
||||
//! Specifies the period in which a reference value will still be alive and safe to read.
|
||||
enum class Lifetime
|
||||
{
|
||||
//! Specifies that the value is safe to read and will remain so indefinitely.
|
||||
//! This implies that the value will not be mutated for the duration of this storage.
|
||||
Persistent,
|
||||
//! Specifies that the value may change or be deallocated, and must be copied to be safely stored.
|
||||
Temporary,
|
||||
};
|
||||
|
||||
//
|
||||
// VisitorErrorCode enum
|
||||
//
|
||||
//! Error code specifying the reason a Visitor operation failed.
|
||||
enum class VisitorErrorCode
|
||||
{
|
||||
//! Set when a Visitor doesn't have an implementation for a given attribute type.
|
||||
//! A pure-JSON serializer might reject a Node attribute, for example, and serialization visitors
|
||||
//! can forbid non-serializable Opaque types.
|
||||
UnsupportedOperation,
|
||||
//! Set when a Visitor has received malformed or invalid data.
|
||||
//! Potential sources include mismatching Begin/End call pairs or invalid attribute or element counts
|
||||
//! being sent to End methods.
|
||||
InvalidData,
|
||||
//! The Visitor failed for some other reason not caused by invalid input.
|
||||
//! If returning a custom error with this code, it's preferrable to also provide supplemental info
|
||||
//! in the form of an explanatory string.
|
||||
InternalError
|
||||
};
|
||||
|
||||
//
|
||||
// VisitorError class
|
||||
//
|
||||
//! Details of the reason for failure within a VisitorInterface operation.
|
||||
class VisitorError final
|
||||
{
|
||||
public:
|
||||
explicit VisitorError(VisitorErrorCode code);
|
||||
VisitorError(VisitorErrorCode code, AZStd::string additionalInfo);
|
||||
|
||||
//! Gets the error code associated with this error.
|
||||
VisitorErrorCode GetCode() const;
|
||||
//! Gets a supplemental error info string from the error.
|
||||
//! Returns an empty string if no additional information was provided to the error.
|
||||
const AZStd::string& GetAdditionalInfo() const;
|
||||
//! Provides a formatted, human-readable error description that can be used for logging purposes.
|
||||
AZStd::string FormatVisitorErrorMessage() const;
|
||||
|
||||
//! Helper method, translates a VisitorErrorCode to a human readable string.
|
||||
static const char* CodeToString(VisitorErrorCode code);
|
||||
|
||||
private:
|
||||
VisitorErrorCode m_code;
|
||||
AZStd::string m_additionalInfo;
|
||||
};
|
||||
|
||||
//! A type alias for opaque DOM types that aren't meant to be serializable.
|
||||
//! /see VisitorInterface::OpaqueValue
|
||||
using OpaqueType = AZStd::any;
|
||||
|
||||
//
|
||||
// VisitorFlags enum
|
||||
//
|
||||
//! Flags representning capabilities of a \ref Visitor.
|
||||
enum class VisitorFlags : AZ::u16
|
||||
{
|
||||
//! No flags are set. This can be used in conjunction with bitwise operators to check a flag.
|
||||
Null = 0,
|
||||
//! If set, this Visitor interface supports raw strings in place of specific value types.
|
||||
//! Visitors with this flag accept RawValue calls in lieu of more specific value calls such as Int64 or String.
|
||||
SupportsRawValues = (1 << 1),
|
||||
//! If set, this Visitor interface supports raw strings in place of Name types for keys and Node names.
|
||||
//! Visitors with this flag accept RawKey and RawStartNode in lieu of Key and StartNode calls.
|
||||
SupportsRawKeys = (1 << 2),
|
||||
//! If set, this Visitor interface supports Object types described via BeginObject and EndObject.
|
||||
SupportsObjects = (1 << 3),
|
||||
//! If set, this Visitor interface supports Array types described via BeginArray and EndArray.
|
||||
SupportsArrays = (1 << 4),
|
||||
//! If set, this Visitor interface supports Node types described BeginNode and EndNode.
|
||||
SupportsNodes = (1 << 4),
|
||||
//! If set, this Visitor interface supports opaque values described via OpaqueValue.
|
||||
SupportsOpaqueValues = (1 << 5),
|
||||
};
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(VisitorFlags);
|
||||
|
||||
//
|
||||
// Visitor class
|
||||
//
|
||||
//! An interface for performing operations on elements of a generic DOM (Document Object Model).
|
||||
//! A Document Object Model is defined here as a tree structure comprised of one of the following values:
|
||||
//! - Primitives: plain data types, including
|
||||
//! - \ref Int64: 64 bit signed integer
|
||||
//! - \ref Uint64: 64 bit unsigned integer
|
||||
//! - \ref Bool: boolean value
|
||||
//! - \ref Double: 64 bit double precision float
|
||||
//! - \ref Null: sentinel "empty" type with no value representation
|
||||
//! - \ref String: UTF8 encoded string
|
||||
//! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type
|
||||
//! (including Object)
|
||||
//! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array)
|
||||
//! - \ref Node: a container
|
||||
//! - \ref OpaqueValue: An arbitrary value stored in an AZStd::any. This is a non-serializable representation of an
|
||||
//! entry useful for in-memory options. This is intended to be used as an intermediate value over the course of DOM
|
||||
//! transformation and as a proxy to pass through types of which the DOM has no knowledge to other systems.
|
||||
//!
|
||||
//! Opaque values are rejected by the default VisitorInterface implementation.
|
||||
//!
|
||||
//! Care should be ensured that DOMs representing opaque types are only visited by consumers that understand them.
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
virtual ~Visitor() = default;
|
||||
|
||||
//! The result of a Visitor operation.
|
||||
//! A failure indicates a non-recoverable issue and signals that no further visit calls may be made in the
|
||||
//! current state.
|
||||
using Result = AZ::Outcome<void, VisitorError>;
|
||||
|
||||
//! Returns a set of flags representing the operations this Visitor supports.
|
||||
//! The base implementation supports raw keys (\see VisitorFlags::SupportsRawKeys) and
|
||||
//! arrays (\see VisitorFlags::SupportsArrays), objects (\see VisitorFlags::SupportsObjects), and
|
||||
//! nodes (\see VisitorFlags::SupportsNodes).
|
||||
//! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues)
|
||||
//! are disallowed by default, as their handling is intended to be implementation-specific.
|
||||
virtual VisitorFlags GetVisitorFlags() const;
|
||||
//! /see VisitorFlags::SupportsRawValues
|
||||
bool SupportsRawValues() const;
|
||||
//! /see VisitorFlags::SupportsRawKeys
|
||||
bool SupportsRawKeys() const;
|
||||
//! /see VisitorFlags::SupportsObjects
|
||||
bool SupportsObjects() const;
|
||||
//! /see VisitorFlags::SupportsArrays
|
||||
bool SupportsArrays() const;
|
||||
//! /see VisitorFlags::SupportsNodes
|
||||
bool SupportsNodes() const;
|
||||
//! /see VisitorFlags::SupportsOpaqueValues
|
||||
bool SupportsOpaqueValues() const;
|
||||
|
||||
//! Operates on an empty null value.
|
||||
virtual Result Null();
|
||||
//! Operates on a bool value.
|
||||
virtual Result Bool(bool value);
|
||||
//! Operates on a signed, 64 bit integer value.
|
||||
virtual Result Int64(AZ::s64 value);
|
||||
//! Operates on an unsigned, 64 bit integer value.
|
||||
virtual Result Uint64(AZ::u64 value);
|
||||
//! Operates on a double precision, 64 bit floating point value.
|
||||
virtual Result Double(double value);
|
||||
//! Operates on a string value. As strings are a reference type.
|
||||
//! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
|
||||
virtual Result String(AZStd::string_view value, Lifetime lifetime);
|
||||
//! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to
|
||||
//! indicate where the value may be stored persistently or requires a copy.
|
||||
//! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special
|
||||
//! cases with specific implementations, not generic usage.
|
||||
//! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
|
||||
virtual Result OpaqueValue(const OpaqueType& value, Lifetime lifetime);
|
||||
//! Operates on a raw value encoded as a UTF-8 string that hasn't had its type deduced.
|
||||
//! Visitors that support raw values (\see VisitorFlags::SupportsRawValues) may parse the raw value and
|
||||
//! forward it to the corresponding value call or calls of their choice.
|
||||
//! The base implementation of RawValue rejects the operation, as raw values are meant to be handled on
|
||||
//! a per-implementation basis.
|
||||
virtual Result RawValue(AZStd::string_view value, Lifetime lifetime);
|
||||
|
||||
//! Operates on an Object.
|
||||
//! Callers may make any number of Key calls, followed by calls representing a value (including a nested
|
||||
//! StartObject call) and then must call EndObject.
|
||||
virtual Result StartObject();
|
||||
//! Finishes operating on an Object.
|
||||
//! Callers must provide the number of attributes that were provided to the object, i.e. the number of key
|
||||
//! and value calls made within the direct context of this object (but not any nested objects / nodes).
|
||||
virtual Result EndObject(AZ::u64 attributeCount);
|
||||
|
||||
//! Specifies a key for a key/value pair.
|
||||
//! Key must be called subsequent to a call to \ref StartObject or \ref StartNode and immediately followed by
|
||||
//! calls representing the key's associated value.
|
||||
virtual Result Key(AZ::Name key);
|
||||
//! Specifies a key for a key/value pair using a raw string instead of \ref AZ::Name.
|
||||
//! \see Key
|
||||
virtual Result RawKey(AZStd::string_view key, Lifetime lifetime);
|
||||
|
||||
//! Operates on an Array.
|
||||
//! Callers may make any number of subsequent value calls to represent the elements of the array, and then must
|
||||
//! call EndArray.
|
||||
virtual Result StartArray();
|
||||
//! Finishes operating on an Array.
|
||||
//! Callers must provide the number of elements that were provided to the array, i.e. the number of value calls
|
||||
//! made within the direct context of this array (but not any nested arrays / nodes).
|
||||
virtual Result EndArray(AZ::u64 elementCount);
|
||||
|
||||
//! Operates on a Node.
|
||||
//! Callers may make any number of Key calls followed by value calls or value calls not prefixed with a Key
|
||||
//! call, and then must call EndNode. See \ref StartObject and \ref StartArray as Node types combine the
|
||||
//! functionality of both structures into a named Node structure.
|
||||
virtual Result StartNode(AZ::Name name);
|
||||
//! Operates on a Node using a raw string instead of \ref AZ::Name.
|
||||
//! \see StartNode
|
||||
virtual Result RawStartNode(AZStd::string_view name, Lifetime lifetime);
|
||||
//! Finishes operating on a Node.
|
||||
//! Callers must provide both the number of attributes the were provided and the number of elements that were
|
||||
//! provided to the node, attributes being values prefaced by a call to Key.
|
||||
virtual Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount);
|
||||
|
||||
protected:
|
||||
Visitor() = default;
|
||||
|
||||
//! Helper method, constructs a failure \ref Result with the specified code.
|
||||
static Result VisitorFailure(VisitorErrorCode code);
|
||||
//! Helper method, constructs a failure \ref Result with the specified code and supplemental info.
|
||||
static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo);
|
||||
//! Helper method, constructs a failure \ref Result with the specified error.
|
||||
static Result VisitorFailure(VisitorError error);
|
||||
//! Helper method, constructs a success \ref Result.
|
||||
static Result VisitorSuccess();
|
||||
};
|
||||
} // namespace AZ::DOM
|
||||
@@ -123,6 +123,8 @@ set(FILES
|
||||
Debug/TraceMessagesDrillerBus.h
|
||||
Debug/TraceReflection.cpp
|
||||
Debug/TraceReflection.h
|
||||
DOM/DomVisitor.cpp
|
||||
DOM/DomVisitor.h
|
||||
Driller/DefaultStringPool.h
|
||||
Driller/Driller.cpp
|
||||
Driller/Driller.h
|
||||
|
||||
Reference in New Issue
Block a user