Merge remote-tracking branch 'upstream/stabilization/2106' into nvsickle/MergeStabilizationJun18

This commit is contained in:
nvsickle
2021-06-18 13:25:53 -07:00
578 changed files with 19526 additions and 20039 deletions
@@ -36,6 +36,12 @@ namespace AZ
Color* color = reinterpret_cast<Color*>(outputValue);
AZ_Assert(color, "Output value for JsonColorSerializer can't be null.");
if (IsExplicitDefault(inputValue))
{
*color = Color::CreateZero();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Color value set to default of zero.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
@@ -43,10 +49,14 @@ namespace AZ
case rapidjson::kObjectType:
return LoadObject(*color, inputValue, context);
case rapidjson::kStringType: // fall through
case rapidjson::kNumberType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Colors can only be read from arrays or objects.");
@@ -91,6 +101,11 @@ namespace AZ
}
}
auto JsonColorSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonColorSerializer::LoadObject(Color& output, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
@@ -28,6 +28,8 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
enum class LoadAlpha
{
@@ -263,7 +263,7 @@ namespace AZ::JsonMathMatrixSerializerInternal
template<typename MatrixType, size_t RowCount, size_t ColumnCount>
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
const rapidjson::Value& inputValue, JsonDeserializerContext& context, bool isExplicitDefault)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
@@ -279,6 +279,12 @@ namespace AZ::JsonMathMatrixSerializerInternal
MatrixType* matrix = reinterpret_cast<MatrixType*>(outputValue);
AZ_Assert(matrix, "Output value for JsonMatrix%zux%zuSerializer can't be null.", RowCount, ColumnCount);
if (isExplicitDefault)
{
*matrix = MatrixType::CreateIdentity();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Matrix value set to identity matrix.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
@@ -381,6 +387,16 @@ namespace AZ::JsonMathMatrixSerializerInternal
namespace AZ
{
// BaseJsonMatrixSerializer
AZ_CLASS_ALLOCATOR_IMPL(BaseJsonMatrixSerializer, SystemAllocator, 0);
auto BaseJsonMatrixSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
// Matrix3x3
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x3Serializer, SystemAllocator, 0);
@@ -389,10 +405,7 @@ namespace AZ
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x3, 3, 3>(
outputValue,
outputValueTypeId,
inputValue,
context);
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonMatrix3x3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -401,11 +414,7 @@ namespace AZ
outputValue.SetObject();
return JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x3>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
outputValue, inputValue, defaultValue, valueTypeId, context);
}
@@ -417,10 +426,7 @@ namespace AZ
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x4, 3, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonMatrix3x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -428,19 +434,11 @@ namespace AZ
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto result =
JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation =
JsonMathMatrixSerializerInternal::StoreTranslation<Matrix3x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
result.GetResultCode().Combine(resultTranslation);
return result;
@@ -454,10 +452,7 @@ namespace AZ
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix4x4, 4, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonMatrix4x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -465,19 +460,11 @@ namespace AZ
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto result =
JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix4x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation =
JsonMathMatrixSerializerInternal::StoreTranslation<Matrix4x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
result.GetResultCode().Combine(resultTranslation);
return result;
@@ -16,11 +16,18 @@
namespace AZ
{
class JsonMatrix3x3Serializer
: public BaseJsonSerializer
class BaseJsonMatrixSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", BaseJsonSerializer);
AZ_RTTI(BaseJsonMatrixSerializer, "{18CA4637-C9B7-454B-9126-107E18A8C096}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
OperationFlags GetOperationsFlags() const override;
};
class JsonMatrix3x3Serializer : public BaseJsonMatrixSerializer
{
public:
AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", BaseJsonMatrixSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -28,11 +35,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonMatrix3x4Serializer
: public BaseJsonSerializer
class JsonMatrix3x4Serializer : public BaseJsonMatrixSerializer
{
public:
AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", BaseJsonSerializer);
AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", BaseJsonMatrixSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -40,11 +46,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonMatrix4x4Serializer
: public BaseJsonSerializer
class JsonMatrix4x4Serializer : public BaseJsonMatrixSerializer
{
public:
AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", BaseJsonSerializer);
AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", BaseJsonMatrixSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -124,7 +124,7 @@ namespace AZ
template<typename VectorType, size_t ElementCount>
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
JsonDeserializerContext& context, bool isExplicitDefault)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
@@ -138,6 +138,12 @@ namespace AZ
VectorType* vector = reinterpret_cast<VectorType*>(outputValue);
AZ_Assert(vector, "Output value for JsonVector%iSerializer can't be null.", ElementCount);
if (isExplicitDefault)
{
*vector = VectorType::CreateZero();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Math vector value set to default of zero.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
@@ -145,10 +151,14 @@ namespace AZ
case rapidjson::kObjectType:
return LoadObject<VectorType, ElementCount>(*vector, inputValue, context);
case rapidjson::kStringType: // fall through
case rapidjson::kNumberType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Math vectors can only be read from arrays or objects.");
@@ -189,6 +199,16 @@ namespace AZ
}
}
// BaseJsonVectorSerializer
AZ_CLASS_ALLOCATOR_IMPL(BaseJsonVectorSerializer, SystemAllocator, 0);
auto BaseJsonVectorSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
// Vector2
@@ -197,7 +217,8 @@ namespace AZ
JsonSerializationResult::Result JsonVector2Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathVectorSerializerInternal::Load<Vector2, 2>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Vector2, 2>(
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonVector2Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -214,7 +235,8 @@ namespace AZ
JsonSerializationResult::Result JsonVector3Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathVectorSerializerInternal::Load<Vector3, 3>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Vector3, 3>(
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonVector3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -231,7 +253,8 @@ namespace AZ
JsonSerializationResult::Result JsonVector4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathVectorSerializerInternal::Load<Vector4, 4>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Vector4, 4>(
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonVector4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -252,7 +275,7 @@ namespace AZ
// check for "yaw, pitch, roll" object
if (inputValue.IsObject())
{
if (inputValue.GetObject().ObjectEmpty())
if (IsExplicitDefault(inputValue))
{
Quaternion* outQuaternion = reinterpret_cast<Quaternion*>(outputValue);
*outQuaternion = Quaternion::CreateIdentity();
@@ -283,7 +306,7 @@ namespace AZ
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read quaternion.");
}
return JsonMathVectorSerializerInternal::Load<Quaternion, 4>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Quaternion, 4>(outputValue, outputValueTypeId, inputValue, context, false);
}
JsonSerializationResult::Result JsonQuaternionSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -16,11 +16,18 @@
namespace AZ
{
class JsonVector2Serializer
: public BaseJsonSerializer
class BaseJsonVectorSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonVector2Serializer, "{E1EAA209-9682-4120-B26B-3EDD9AD56D6F}", BaseJsonSerializer);
AZ_RTTI(BaseJsonVectorSerializer, "{C188D355-E6DF-4590-8B31-F40591F48A8E}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
OperationFlags GetOperationsFlags() const override;
};
class JsonVector2Serializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonVector2Serializer, "{E1EAA209-9682-4120-B26B-3EDD9AD56D6F}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -28,11 +35,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonVector3Serializer
: public BaseJsonSerializer
class JsonVector3Serializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonVector3Serializer, "{BF82BBF3-3CD9-48DA-97CC-E4DF2EF01552}", BaseJsonSerializer);
AZ_RTTI(JsonVector3Serializer, "{BF82BBF3-3CD9-48DA-97CC-E4DF2EF01552}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -40,11 +46,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonVector4Serializer
: public BaseJsonSerializer
class JsonVector4Serializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonVector4Serializer, "{05B45EA7-7102-4281-8AA0-2AC72D74AAFD}", BaseJsonSerializer);
AZ_RTTI(JsonVector4Serializer, "{05B45EA7-7102-4281-8AA0-2AC72D74AAFD}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -52,11 +57,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonQuaternionSerializer
: public BaseJsonSerializer
class JsonQuaternionSerializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonQuaternionSerializer, "{18604375-3606-49AC-B366-0F6DF9149FF3}", BaseJsonSerializer);
AZ_RTTI(JsonQuaternionSerializer, "{18604375-3606-49AC-B366-0F6DF9149FF3}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -33,6 +33,12 @@ namespace AZ
AZ::Transform* transformInstance = reinterpret_cast<AZ::Transform*>(outputValue);
AZ_Assert(transformInstance, "Output value for JsonTransformSerializer can't be null.");
if (IsExplicitDefault(inputValue))
{
*transformInstance = AZ::Transform::CreateIdentity();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Transform value set to identity.");
}
JSR::ResultCode result(JSR::Tasks::ReadField);
{
@@ -72,7 +78,7 @@ namespace AZ
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Succesfully loaded Transform information."
result.GetProcessing() != JSR::Processing::Halted ? "Successfully loaded Transform information."
: "Failed to load Transform information.");
}
@@ -140,4 +146,9 @@ namespace AZ
: "Failed to store Transform information.");
}
auto JsonTransformSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
} // namespace AZ
@@ -30,6 +30,8 @@ namespace AZ
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId,
JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
// Note: These need to be defined as "const char[]" instead of "const char*" so that they can be implicitly converted
// to a rapidjson::GenericStringRef<>. (This also lets rapidjson get the string length at compile time)
@@ -33,6 +33,11 @@ namespace AZ
AZStd::regex_constants::icase | AZStd::regex_constants::optimize);
}
auto JsonUuidSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonUuidSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
@@ -53,13 +58,24 @@ namespace AZ
Uuid* valAsUuid = reinterpret_cast<Uuid*>(outputValue);
if (IsExplicitDefault(inputValue))
{
*valAsUuid = AZ::Uuid::CreateNull();
return MessageResult("Uuid value set to default of null.", JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed));
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType: // fallthrough
case rapidjson::kObjectType:// fallthrough
case rapidjson::kFalseType: // fallthrough
case rapidjson::kTrueType: // fallthrough
case rapidjson::kNumberType:// fallthrough
case rapidjson::kArrayType:
[[fallthrough]];
case rapidjson::kObjectType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
return MessageResult("Unsupported type. Uuids can only be read from strings.",
JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported));
@@ -41,6 +41,8 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
//! Does the same as load, but doesn't report through the provided callback in the settings. Instead the final
//! ResultCode and message are returned and it's up to the caller to report if need needed.
MessageResult UnreportedLoad(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue);
@@ -406,6 +406,7 @@ namespace AZ
AZStd::vector<AZ::BehaviorParameter> eventParamsTypes{ AZStd::initializer_list<AZ::BehaviorParameter>{
CreateBehaviorEventParameter<decay_array<T>>()... } };
behaviorContext->Class<AZ::Event<T...>>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
->Attribute(AZ::Script::Attributes::EventHandlerCreationFunction, createHandlerHolder)
->Attribute(AZ::Script::Attributes::EventParameterTypes, eventParamsTypes)
->Method("HasHandlerConnected", &AZ::Event<T...>::HasHandlerConnected)
@@ -413,6 +414,7 @@ namespace AZ
behaviorContext->Class<AZ::EventHandler<T...>>()
->Method("Disconnect", &AZ::EventHandler<T...>::Disconnect)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
;
}
}
@@ -32,13 +32,24 @@ namespace AZ
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
return LoadContainer(outputValue, outputValueTypeId, inputValue, false, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
if (IsExplicitDefault(inputValue))
{
// Because this serializer has only the operation flag "InitializeNewInstance" set, the only time this will be called with
// an explicit default is when a new instance has been created.
return LoadContainer(outputValue, outputValueTypeId, inputValue, true, context);
}
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. AZStd::array entries can only be read from an array.");
@@ -129,7 +140,16 @@ namespace AZ
}
}
JsonSerializationResult::Result JsonArraySerializer::LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
auto JsonArraySerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonArraySerializer::LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used to remove name conflicts in AzCore in uber builds.
@@ -154,14 +174,7 @@ namespace AZ
"Unable to retrieve the correct container information for AZStd::array instance.");
}
const size_t size = container->Size(outputValue);
if (inputValue.Size() < size)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in JSON array to load an AZStd::array from.");
}
ContinuationFlags flags = ContinuationFlags::None;
ContinuationFlags flags = isNewInstance ? ContinuationFlags::LoadAsNewInstance : ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -175,13 +188,23 @@ namespace AZ
};
container->EnumTypes(typeEnumCallback);
const size_t size = container->Size(outputValue);
if (!isNewInstance && inputValue.Size() < size)
{
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, "Not enough entries in JSON array to load an AZStd::array from.");
}
rapidjson::Value explicitDefaultValue = GetExplicitDefault();
JSR::ResultCode retVal(JSR::Tasks::ReadField);
for (size_t i = 0; i < size; ++i)
{
ScopedContextPath subPath(context, i);
void* element = container->GetElementByIndex(outputValue, nullptr, i);
JSR::ResultCode result = ContinueLoading(element, elementTypeId, inputValue[aznumeric_caster(i)], context, flags);
JSR::ResultCode result = ContinueLoading(
element, elementTypeId, isNewInstance ? explicitDefaultValue : inputValue[aznumeric_caster(i)], context, flags);
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(result, "Failed to load data to element in AZStd::array.");
@@ -189,15 +212,19 @@ namespace AZ
retVal.Combine(result);
}
if (container->Size(outputValue) == inputValue.Size())
if (isNewInstance)
{
return context.Report(retVal, "Filled new instance of AZStd::array with defaults.");
}
else if (container->Size(outputValue) == inputValue.Size())
{
return context.Report(retVal, "Successfully read entries into AZStd::array.");
}
else
{
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Skipped));
return context.Report(retVal,
"Successfully read available entries into AZStd::array, but there were still values left in the JSON array.");
return context.Report(
retVal, "Successfully read available entries into AZStd::array, but there were still values left in the JSON array.");
}
}
} // namespace AZ
@@ -30,8 +30,14 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
protected:
JsonSerializationResult::Result LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonSerializationResult::Result LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -216,9 +216,10 @@ namespace AZ
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
{
bool loadAsNewInstance = (flags & ContinuationFlags::LoadAsNewInstance) == ContinuationFlags::LoadAsNewInstance;
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
: JsonDeserializer::Load(object, typeId, value, context);
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
@@ -163,15 +163,20 @@ namespace AZ
enum class ContinuationFlags
{
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1 //! The default value provided for storing will be replaced with a newly created one.
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one.
LoadAsNewInstance = 1 << 2 //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
//! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to
//! an array.
};
enum class OperationFlags
{
None = 0, //! No flags that control how the custom json serializer is used.
ManualDefault = 1 << 0 //! Even if an (explicit) default is found the custom json serializer will still be called.
None = 0, //! No flags that control how the custom json serializer is used.
ManualDefault = 1 << 0, //! Even if an (explicit) default is found the custom json serializer will still be called.
InitializeNewInstance = 1 << 1 //! If set, the custom json serializer will be called with an explicit default if a new
//! instance of its target type is created.
};
virtual ~BaseJsonSerializer() = default;
@@ -34,11 +34,16 @@ namespace AZ
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Basic containers can only be read from an array.");
@@ -123,6 +128,10 @@ namespace AZ
{
if (retVal.HasDoneWork())
{
// If at least one value was written, even if it has all defaults, then the array has
// a value written to it and is therefore not in a default state anymore.
retVal.Combine(JSR::ResultCode(JSR::Tasks::WriteValue, JSR::Outcomes::Success));
outputValue = AZStd::move(array);
return context.Report(retVal, "Content written to basic container.");
}
@@ -165,6 +174,7 @@ namespace AZ
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
flags |= ContinuationFlags::LoadAsNewInstance;
const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits<size_t>::max();
@@ -243,6 +253,11 @@ namespace AZ
}
size_t addedCount = container->Size(outputValue) - containerSize;
if (addedCount > 0)
{
// Values were added which means the container is no longer in its default state of being empty.
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
AZStd::string_view message =
addedCount >= arraySize ? "Successfully read basic container.":
addedCount == 0 ? "Unable to read data for basic container." :
@@ -82,12 +82,18 @@ namespace AZ
bool* valAsBool = reinterpret_cast<bool*>(outputValue);
if (IsExplicitDefault(inputValue))
{
*valAsBool = {};
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Boolean value set to default of 'false'.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
// fallthrough
[[fallthrough]];
case rapidjson::kObjectType:
// fallthrough
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Booleans can't be read from arrays, objects or null.");
@@ -96,7 +102,7 @@ namespace AZ
return SerializerInternal::TextToValue(valAsBool, inputValue.GetString(), inputValue.GetStringLength(), context);
case rapidjson::kFalseType:
// fallthrough
[[fallthrough]];
case rapidjson::kTrueType:
*valAsBool = inputValue.GetBool();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read boolean.");
@@ -145,4 +151,9 @@ namespace AZ
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default boolean used.");
}
auto JsonBoolSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
} // namespace AZ
@@ -27,5 +27,6 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
} // namespace AZ
@@ -62,19 +62,26 @@ namespace AZ
}
template <typename T>
static JsonSerializationResult::Result Load(T* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
static JsonSerializationResult::Result Load(
T* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context, bool isExplicitDefault)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
static_assert(AZStd::is_floating_point<T>::value, "Expected T to be a floating point type");
AZ_Assert(outputValue, "Expected a valid pointer to load from json value.");
if (isExplicitDefault)
{
*outputValue = {};
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Floating point value set to default of 0.0.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
// fallthrough
[[fallthrough]];
case rapidjson::kObjectType:
// fallthrough
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Floating point values can't be read from arrays, objects or null.");
@@ -83,7 +90,7 @@ namespace AZ
return TextToValue(outputValue, inputValue.GetString(), context);
case rapidjson::kFalseType:
// fallthrough
[[fallthrough]];
case rapidjson::kTrueType:
*outputValue = inputValue.GetBool() ? 1.0f : 0.0f;
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success,
@@ -144,7 +151,8 @@ namespace AZ
"Unable to deserialize double to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerFloatingPointInternal::Load(reinterpret_cast<double*>(outputValue), inputValue, context);
return SerializerFloatingPointInternal::Load(
reinterpret_cast<double*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonDoubleSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -156,6 +164,11 @@ namespace AZ
return SerializerFloatingPointInternal::Store<double>(outputValue, inputValue, defaultValue, context);
}
auto JsonDoubleSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonFloatSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
@@ -163,7 +176,8 @@ namespace AZ
"Unable to deserialize float to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerFloatingPointInternal::Load(reinterpret_cast<float*>(outputValue), inputValue, context);
return SerializerFloatingPointInternal::Load(
reinterpret_cast<float*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonFloatSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -174,4 +188,9 @@ namespace AZ
AZ_UNUSED(valueTypeId);
return SerializerFloatingPointInternal::Store<float>(outputValue, inputValue, defaultValue, context);
}
auto JsonFloatSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
} // namespace AZ
@@ -28,6 +28,7 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
class JsonFloatSerializer
@@ -40,5 +41,6 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
} // namespace AZ
@@ -25,6 +25,8 @@
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(BaseJsonIntegerSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonCharSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonShortSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonIntSerializer, SystemAllocator, 0);
@@ -56,19 +58,25 @@ namespace AZ
template <typename T>
static JsonSerializationResult::Result LoadInt(T* outputValue, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
JsonDeserializerContext& context, bool isDefaultValue)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
static_assert(AZStd::is_integral<T>(), "Expected T to be a signed or unsigned type");
AZ_Assert(outputValue, "Expected a valid pointer to load from json value.");
if (isDefaultValue)
{
*outputValue = {};
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Integer value set to default of zero.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
// fallthrough
[[fallthrough]];
case rapidjson::kObjectType:
// fallthrough
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Integers can't be read from arrays, objects or null.");
@@ -77,7 +85,7 @@ namespace AZ
return TextToValue(outputValue, inputValue.GetString(), context);
case rapidjson::kFalseType:
// fallthrough
[[fallthrough]];
case rapidjson::kTrueType:
*outputValue = inputValue.GetBool() ? 1 : 0;
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success,
@@ -125,6 +133,11 @@ namespace AZ
}
} // namespace SerializerInternal
auto BaseJsonIntegerSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonCharSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
@@ -132,7 +145,7 @@ namespace AZ
"Unable to deserialize char to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<char*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<char*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonCharSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -151,7 +164,7 @@ namespace AZ
"Unable to deserialize short to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<short*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<short*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonShortSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -170,7 +183,7 @@ namespace AZ
"Unable to deserialize int to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<int*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<int*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonIntSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -189,7 +202,7 @@ namespace AZ
"Unable to deserialize long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -208,7 +221,7 @@ namespace AZ
"Unable to deserialize long long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<long long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<long long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonLongLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -227,7 +240,8 @@ namespace AZ
"Unable to deserialize unsigned char to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned char*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned char*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedCharSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -246,7 +260,8 @@ namespace AZ
"Unable to deserialize unsigned short to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned short*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned short*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedShortSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -265,7 +280,8 @@ namespace AZ
"Unable to deserialize unsigned int to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned int*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned int*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedIntSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -284,7 +300,8 @@ namespace AZ
"Unable to deserialize unsigned long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -303,7 +320,8 @@ namespace AZ
"Unable to deserialize unsigned long long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned long long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned long long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedLongLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -18,11 +18,18 @@
namespace AZ
{
class JsonCharSerializer
: public BaseJsonSerializer
class BaseJsonIntegerSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonCharSerializer, "{CA2A4AAC-3068-40B2-94F8-A537FBA8236E}", BaseJsonSerializer);
AZ_RTTI(BaseJsonIntegerSerializer, "{FD060F54-D3B5-4D5B-B64A-AFE371CD6F20}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
OperationFlags GetOperationsFlags() const override;
};
class JsonCharSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonCharSerializer, "{CA2A4AAC-3068-40B2-94F8-A537FBA8236E}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -30,11 +37,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonShortSerializer
: public BaseJsonSerializer
class JsonShortSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonShortSerializer, "{3D6789BD-231B-4E5D-B81D-609E71A2BCB5}", BaseJsonSerializer);
AZ_RTTI(JsonShortSerializer, "{3D6789BD-231B-4E5D-B81D-609E71A2BCB5}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -42,11 +48,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonIntSerializer
: public BaseJsonSerializer
class JsonIntSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonIntSerializer, "{29E26946-0F1F-44B0-A098-1171B7B0C8FA}", BaseJsonSerializer);
AZ_RTTI(JsonIntSerializer, "{29E26946-0F1F-44B0-A098-1171B7B0C8FA}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -54,11 +59,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonLongSerializer
: public BaseJsonSerializer
class JsonLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonLongSerializer, "{0EB432D0-A0C8-43B2-9D65-A73A4D6DFE3E}", BaseJsonSerializer);
AZ_RTTI(JsonLongSerializer, "{0EB432D0-A0C8-43B2-9D65-A73A4D6DFE3E}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -66,11 +70,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonLongLongSerializer
: public BaseJsonSerializer
class JsonLongLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonLongLongSerializer, "{5E7967DE-A4DC-40E1-81A1-2896A054BB8A}", BaseJsonSerializer);
AZ_RTTI(JsonLongLongSerializer, "{5E7967DE-A4DC-40E1-81A1-2896A054BB8A}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -78,11 +81,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedCharSerializer
: public BaseJsonSerializer
class JsonUnsignedCharSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedCharSerializer, "{1E6D606F-8490-4736-AAFF-91046FDEA2BB}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedCharSerializer, "{1E6D606F-8490-4736-AAFF-91046FDEA2BB}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -90,11 +92,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedShortSerializer
: public BaseJsonSerializer
class JsonUnsignedShortSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedShortSerializer, "{3C92D2CC-CB13-4A40-B779-47562EE36451}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedShortSerializer, "{3C92D2CC-CB13-4A40-B779-47562EE36451}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -102,11 +103,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedIntSerializer
: public BaseJsonSerializer
class JsonUnsignedIntSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedIntSerializer, "{70C0714A-690D-4F30-8986-ABC9DEFE9D62}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedIntSerializer, "{70C0714A-690D-4F30-8986-ABC9DEFE9D62}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -114,11 +114,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedLongSerializer
: public BaseJsonSerializer
class JsonUnsignedLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedLongSerializer, "{28E5499F-6AF4-4778-AE14-66BA40B56247}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedLongSerializer, "{28E5499F-6AF4-4778-AE14-66BA40B56247}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -126,11 +125,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedLongLongSerializer
: public BaseJsonSerializer
class JsonUnsignedLongLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedLongLongSerializer, "{AB048BB3-C280-4166-9E2E-54CE2C3413CA}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedLongLongSerializer, "{AB048BB3-C280-4166-9E2E-54CE2C3413CA}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -19,25 +19,30 @@
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
JsonSerializationResult::ResultCode JsonDeserializer::DeserializerDefaultCheck(BaseJsonSerializer* serializer, void* object,
const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context)
const Uuid& typeId,const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
bool isExplicitDefault = IsExplicitDefault(value);
bool manuallyDefaults = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::ManualDefault) ==
BaseJsonSerializer::OperationFlags::ManualDefault;
return !isExplicitDefault || (isExplicitDefault && manuallyDefaults)
bool initializeNewInstance = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::InitializeNewInstance) ==
BaseJsonSerializer::OperationFlags::InitializeNewInstance;
return
!isExplicitDefault || (isExplicitDefault && manuallyDefaults) || (isExplicitDefault && isNewInstance && initializeNewInstance)
? serializer->Load(object, typeId, value, context)
: context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
JsonSerializationResult::ResultCode JsonDeserializer::Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context)
JsonSerializationResult::ResultCode JsonDeserializer::Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
@@ -50,7 +55,7 @@ namespace AZ
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
{
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
const SerializeContext::ClassData* classData = context.GetSerializeContext()->FindClassData(typeId);
@@ -72,7 +77,7 @@ namespace AZ
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
if (serializer)
{
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
}
@@ -133,7 +138,7 @@ namespace AZ
const SerializeContext::ClassData* resolvedClassData = context.GetSerializeContext()->FindClassData(resolvedTypeId);
if (resolvedClassData)
{
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, context);
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, context);
*objectPtr = resolvedClassData->m_azRtti->Cast(*objectPtr, typeId);
@@ -174,7 +179,7 @@ namespace AZ
}
else
{
return Load(object, classElement.m_typeId, value, context);
return Load(object, classElement.m_typeId, value, false, context);
}
}
@@ -591,7 +596,9 @@ namespace AZ
}
else
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, "Serialization information for target type not found.");
using ReporterString = AZStd::fixed_string<1024>;
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
ReporterString::format("Serialization information for target type %s not found.", loadedTypeId.m_typeId.ToString<ReporterString>().c_str()));
return ResolvePointerResult::FullyProcessed;
}
objectType = loadedTypeId.m_typeId;
@@ -58,8 +58,8 @@ namespace AZ
JsonDeserializer(const JsonDeserializer& rhs) = delete;
JsonDeserializer(JsonDeserializer&& rhs) = delete;
static JsonSerializationResult::ResultCode Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode LoadToPointer(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context);
@@ -120,6 +120,7 @@ namespace AZ
void* object,
const Uuid& typeId,
const rapidjson::Value& value,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -607,10 +607,12 @@ namespace AZ
}
else if (source.Size() > target.Size())
{
rapidjson::SizeType sourceCount = source.Size();
for (rapidjson::SizeType i = count; i < sourceCount; ++i)
// Loop backwards through the removals so that each removal has a valid index when processing in order.
for (rapidjson::SizeType i = source.Size(); i > count; --i)
{
ScopedStackedString entryName(element, i);
// (We use "i - 1" here instead of in the loop to ensure we don't wrap around our unsigned numbers in the case
// where count is 0.)
ScopedStackedString entryName(element, i - 1);
patch.PushBack(CreatePatchInternal_Remove(allocator, element), allocator);
resultCode.Combine(settings.m_reporting("Removed member from array in JSON Patch.",
ResultCode(Tasks::CreatePatch, Outcomes::Success), element));
@@ -249,7 +249,7 @@ namespace AZ
{
StackedString path(StackedString::Format::JsonPointer);
JsonDeserializerContext context(settings);
result = JsonDeserializer::Load(object, objectType, root, context);
result = JsonDeserializer::Load(object, objectType, root, false, context);
}
return result;
}
@@ -190,6 +190,12 @@ namespace AZ
}
size_t addedCount = container->Size(outputValue) - containerSize;
if (addedCount > 0)
{
// If at least one entry was added then the map is no longer in it's default state so
// mark is with success so the result can at best be partial defaults.
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
AZStd::string_view message =
addedCount >= maximumSize ? "Successfully read associative container." :
addedCount == 0 ? "Unable to read data for the associative container." :
@@ -215,10 +221,10 @@ namespace AZ
// Load key
void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0);
AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key.");
ContinuationFlags keyLoadFlags = ContinuationFlags::None;
ContinuationFlags keyLoadFlags = ContinuationFlags::LoadAsNewInstance;
if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
keyLoadFlags = ContinuationFlags::ResolvePointer;
keyLoadFlags |= ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(keyAddress) = nullptr;
}
JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags);
@@ -231,10 +237,10 @@ namespace AZ
// Load value
void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1);
AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value.");
ContinuationFlags valueLoadFlags = ContinuationFlags::None;
ContinuationFlags valueLoadFlags = ContinuationFlags::LoadAsNewInstance;
if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
valueLoadFlags = ContinuationFlags::ResolvePointer;
valueLoadFlags |= ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(valueAddress) = nullptr;
}
JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags);
@@ -34,13 +34,24 @@ namespace AZ
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
return LoadContainer(outputValue, outputValueTypeId, inputValue, false, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
if (IsExplicitDefault(inputValue))
{
// Because this serializer has only the operation flag "InitializeNewInstance" set, the only time this will be called with
// an explicit default is when a new instance has been created.
return LoadContainer(outputValue, outputValueTypeId, inputValue, true, context);
}
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. AZStd::pair or AZStd::tuple can only be read from an array.");
@@ -127,8 +138,13 @@ namespace AZ
}
}
auto JsonTupleSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonTupleSerializer::LoadContainer(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
const rapidjson::Value& inputValue, bool isNewInstance, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used to remove name conflicts in AzCore in uber builds.
@@ -154,13 +170,6 @@ namespace AZ
};
container->EnumTypes(typeCountCallback);
rapidjson::SizeType arraySize = inputValue.Size();
if (arraySize < typeCount)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in array to load an AZStd::pair or AZStd::tuple from.");
}
AZStd::vector<const SerializeContext::ClassElement*> classElements;
classElements.reserve(typeCount);
auto typeEnumCallback = [&classElements](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
@@ -171,46 +180,80 @@ namespace AZ
container->EnumTypes(typeEnumCallback);
JSR::ResultCode retVal(JSR::Tasks::ReadField);
rapidjson::SizeType arrayIndex = 0;
size_t numElementsWritten = 0;
for (size_t i = 0; i < typeCount; ++i)
if (isNewInstance)
{
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
rapidjson::Value explicitDefaultValue = GetExplicitDefault();
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
while (arrayIndex < inputValue.Size())
for (size_t i = 0; i < typeCount; ++i)
{
JSR::ResultCode result = ContinueLoading(elementAddress, classElements[i]->m_typeId, inputValue[arrayIndex], context, flags);
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
ContinuationFlags flags = ContinuationFlags::LoadAsNewInstance;
flags |=
(classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? ContinuationFlags::ResolvePointer
: ContinuationFlags::None);
JSR::ResultCode result = ContinueLoading(elementAddress, classElements[i]->m_typeId, explicitDefaultValue, context, flags);
retVal.Combine(result);
arrayIndex++;
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(retVal, "Failed to read element for AZStd::pair or AZStd::tuple.");
}
else if (result.GetProcessing() != JSR::Processing::Altered)
{
numElementsWritten++;
break;
}
}
}
if (numElementsWritten < typeCount)
{
AZStd::string_view message = numElementsWritten == 0 ?
"Unable to read data for AZStd::pair or AZStd::tuple." :
"Partially read data for AZStd::pair or AZStd::tuple.";
return context.Report(retVal, message);
return context.Report(retVal, "Initialized AZStd::pair or AZStd::tuple to defaults.");
}
else
{
return context.Report(retVal, "Successfully read AZStd::pair or AZStd::tuple.");
if (inputValue.Size() < typeCount)
{
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in array to load an AZStd::pair or AZStd::tuple from.");
}
rapidjson::SizeType arrayIndex = 0;
size_t numElementsWritten = 0;
for (size_t i = 0; i < typeCount; ++i)
{
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
ContinuationFlags flags =
(classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? ContinuationFlags::ResolvePointer
: ContinuationFlags::None);
while (arrayIndex < inputValue.Size())
{
JSR::ResultCode result =
ContinueLoading(elementAddress, classElements[i]->m_typeId, inputValue[arrayIndex], context, flags);
retVal.Combine(result);
arrayIndex++;
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(retVal, "Failed to read element for AZStd::pair or AZStd::tuple.");
}
else if (result.GetProcessing() != JSR::Processing::Altered)
{
numElementsWritten++;
break;
}
}
}
if (numElementsWritten < typeCount)
{
AZStd::string_view message = numElementsWritten == 0 ? "Unable to read data for AZStd::pair or AZStd::tuple."
: "Partially read data for AZStd::pair or AZStd::tuple.";
return context.Report(retVal, message);
}
else
{
return context.Report(retVal, "Successfully read AZStd::pair or AZStd::tuple.");
}
}
}
} // namespace AZ
@@ -23,13 +23,20 @@ namespace AZ
public:
AZ_RTTI(JsonTupleSerializer, "{1AA0ADC1-395A-4223-8A73-304ACDEE7793}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
JsonSerializationResult::Result LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonSerializationResult::Result LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -816,7 +816,11 @@ namespace AZ::SettingsRegistryMergeUtils
}
}
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine, bool executeCommands)
// This function intentionally copies `commandLine`. It looks like it only uses it as a const reference, but the
// code in the loop makes calls that mutates the `commandLine` instance, invalidating the iterators. Making a copy
// ensures that the iterators remain valid.
// NOLINTNEXTLINE(performance-unnecessary-value-param)
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, AZ::CommandLine commandLine, bool executeCommands)
{
// Iterate over all the command line options in order to parse the --regset and --regremove
// arguments in the order they were supplied
@@ -831,7 +835,7 @@ namespace AZ::SettingsRegistryMergeUtils
continue;
}
}
if (commandArgument.m_option == "regremove")
else if (commandArgument.m_option == "regremove")
{
if (!registry.Remove(commandArgument.m_value))
{
@@ -15,11 +15,7 @@
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Settings/SettingsRegistry.h>
namespace AZ
{
class CommandLine;
}
#include <AzCore/Settings/CommandLine.h>
namespace AZ::IO
{
@@ -217,7 +213,7 @@ namespace AZ::SettingsRegistryMergeUtils
//! example: --regdump /My/Array/With/Objects
//! --regdumpall Dumps the entire settings registry to output.
//! Note that this function is only called in development builds and is compiled out in release builds.
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine, bool executeCommands);
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, AZ::CommandLine commandLine, bool executeCommands);
//! Stores the command line settings into the Setting Registry
//! The arguments can be used later anywhere the command line is needed
@@ -168,6 +168,10 @@ namespace JsonSerializationTests
{
features.EnableJsonType(rapidjson::kObjectType);
features.m_typeToInject = rapidjson::kNullType;
// Assets are not fully registered with the Serialize Context for historical reasons. Due to the missing
// information the Json Serializer Conformity Tests can't run the subsection of tests that explicitly
// require the missing information.
features.m_enableNewInstanceTests = false;
}
bool AreEqual(const Asset& lhs, const Asset& rhs) override
@@ -32,6 +32,11 @@ namespace JsonSerializationTests
AZ::NameDictionary::Destroy();
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context)
{
AZ::Name::Reflect(context.get());
}
void Reflect(AZStd::unique_ptr<AZ::JsonRegistrationContext>& context)
{
AZ::Name::Reflect(context.get());
@@ -126,9 +126,9 @@ namespace JsonSerializationTests
{
auto array = AZStd::shared_ptr<Array>(new Array(), Deleter);
(*array)[0] = nullptr;
(*array)[1] = aznew MultipleInheritence();
(*array)[1] = nullptr;
(*array)[2] = nullptr;
(*array)[3] = aznew MultipleInheritence();
(*array)[3] = nullptr;
return array;
}
@@ -154,6 +154,7 @@ namespace JsonSerializationTests
null,
null,
{
"$type": "MultipleInheritence",
"base_var": 242.0,
"var1" : 142
}
@@ -246,56 +247,21 @@ namespace JsonSerializationTests
])";
}
AZStd::string_view GetJsonFor_Store_SerializeFullySetInstance() override
{
// This is a unique situation because the $type is determined separate from other values, so all
// member values can be changed, but since the default type matches the stored type the $type
// will only be written if default values are explicitly kept.
return R"(
[
{
"$type": "MultipleInheritence",
"base_var": 1142.0,
"base2_var1": 1242.0,
"base2_var2": 1342.0,
"base2_var3": 1442.0,
"var1" : 1542,
"var2" : 1642.0
},
{
"base_var": 2142.0,
"base2_var1": 2242.0,
"base2_var2": 2342.0,
"base2_var3": 2442.0,
"var1" : 2542,
"var2" : 2642.0
},
{
"$type": "MultipleInheritence",
"base_var": 3142.0,
"base2_var1": 3242.0,
"base2_var2": 3342.0,
"base2_var3": 3442.0,
"var1" : 3542,
"var2" : 3642.0
},
{
"base_var": 4142.0,
"base2_var1": 4242.0,
"base2_var2": 4342.0,
"base2_var3": 4442.0,
"var1" : 4542,
"var2" : 4642.0
}
])";
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
{
Base::Reflect(context);
MultipleInheritence::Reflect(context, true);
}
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
{
Base::ConfigureFeatures(features);
// These tests don't work with pointers because there'll be a random value in the pointer
// which the Json Serialization will try to delete. The POD version of these tests already cover
// these cases.
features.m_enableNewInstanceTests = false;
}
bool AreEqual(const Array& lhs, const Array& rhs) override
{
size_t size = lhs.size();
@@ -311,6 +277,11 @@ namespace JsonSerializationTests
return rhs[i] == nullptr;
}
if (rhs[i] == nullptr)
{
return false;
}
if (!static_cast<const MultipleInheritence*>(lhs[i])->Equals(*static_cast<const MultipleInheritence*>(rhs[i]), true))
{
return false;
@@ -54,6 +54,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<Container>(Container{ 188, 288, 388 });
}
AZStd::shared_ptr<Container> CreateSingleArrayDefaultInstance() override
{
return AZStd::make_shared<Container>(Container{ 0 });
}
AZStd::string_view GetJsonForFullySetInstance() override
{
return "[188, 288, 388]";
@@ -120,6 +125,13 @@ namespace JsonSerializationTests
&SimplePointerTestDescription::Delete);
}
AZStd::shared_ptr<Container> CreateSingleArrayDefaultInstance() override
{
int* value = reinterpret_cast<int*>(azmalloc(sizeof(int), alignof(int)));
*value = 0;
return AZStd::shared_ptr<Container>(new Container{ value }, &SimplePointerTestDescription::Delete);
}
AZStd::string_view GetJsonForFullySetInstance() override
{
return "[188, 288, 388]";
@@ -180,6 +192,13 @@ namespace JsonSerializationTests
return instance;
}
AZStd::shared_ptr<Container> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::make_shared<Container>();
*instance = { SimpleClass{} };
return instance;
}
AZStd::string_view GetJsonForFullySetInstance() override
{
return R"([
@@ -301,7 +320,7 @@ namespace JsonSerializationTests
ResultCode result = m_serializer->Store(*m_jsonDocument, &instance, &instance, azrtti_typeid(&instance), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq("[{}]");
}
@@ -315,7 +334,7 @@ namespace JsonSerializationTests
ResultCode result = m_serializer->Store(*m_jsonDocument, &instance, nullptr, azrtti_typeid(&instance), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq("[{},{}]");
}
@@ -330,7 +349,6 @@ namespace JsonSerializationTests
ResultCode result = m_serializer->Store(*m_jsonDocument, &instance, nullptr, azrtti_typeid(&instance), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
EXPECT_NE(Outcomes::DefaultsUsed, result.GetOutcome());
Expect_DocStrEq(R"([{"$type": "SimpleInheritence"},{"$type": "SimpleInheritence"}])");
}
@@ -63,6 +63,18 @@ namespace JsonSerializationTests
: public BaseJsonSerializerFixture
{
public:
struct BoolPointerWrapper
{
AZ_TYPE_INFO(BoolPointerWrapper, "{2E67C069-BB0F-4F00-A704-E964F5FE5ED2}");
bool* m_value{ nullptr };
~BoolPointerWrapper()
{
azfree(m_value);
}
};
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
@@ -75,6 +87,12 @@ namespace JsonSerializationTests
BaseJsonSerializerFixture::TearDown();
}
void RegisterAdditional(AZStd::unique_ptr<AZ::SerializeContext>& serializeContext) override
{
serializeContext->Class<BoolPointerWrapper>()
->Field("Value", &BoolPointerWrapper::m_value);
}
void Load(rapidjson::Value& testVal, bool expectedBool, AZ::JsonSerializationResult::Outcomes expectedOutcome)
{
using namespace AZ::JsonSerializationResult;
@@ -32,7 +32,7 @@ namespace JsonSerializationTests
AZStd::shared_ptr<FloatingPointType> CreateDefaultInstance() override
{
return AZStd::make_shared<FloatingPointType>(-2.0f);
return AZStd::make_shared<FloatingPointType>(0.0f);
}
AZStd::shared_ptr<FloatingPointType> CreateFullySetInstance() override
@@ -71,6 +71,20 @@ namespace JsonSerializationTests
: public BaseJsonSerializerFixture
{
public:
struct DoublePointerWrapper
{
AZ_TYPE_INFO(DoublePointerWrapper, "{C2FD9E0B-2641-4D24-A3D9-A29FD1A21A81}");
double* m_double{ nullptr };
float* m_float{ nullptr };
~DoublePointerWrapper()
{
azfree(m_float);
azfree(m_double);
}
};
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
@@ -85,6 +99,13 @@ namespace JsonSerializationTests
BaseJsonSerializerFixture::TearDown();
}
void RegisterAdditional(AZStd::unique_ptr<AZ::SerializeContext>& serializeContext) override
{
serializeContext->Class<DoublePointerWrapper>()
->Field("Double", &DoublePointerWrapper::m_double)
->Field("Float", &DoublePointerWrapper::m_float);
}
void TestSerializers(rapidjson::Value& testVal, double expectedValue, AZ::JsonSerializationResult::Outcomes expectedOutcome)
{
using namespace AZ::JsonSerializationResult;
@@ -62,6 +62,14 @@ namespace JsonSerializationTests
//! can be used to manually create these documents. If that is also not an option the tests can be
//! disabled by setting this flag to false.
bool m_supportsInjection{ true };
//! Enables the check that tries to determine if variables are initialized and if not whether they have the
//! OperationFlags::ManualDefault set. This applies for instance to integers, which won't be initialized if
//! constructed a new instance is created for pointers.
bool m_enableInitializationTest{ true };
//! Enable the test that creates a new instance of the provided test type through the factory that's found in
//! the Serialize Context. This test is automatically disabled for classes that don't have a factory or
//! have a null factory as well as for classes that have mandatory fields.
bool m_enableNewInstanceTests{ true };
private:
// There's no way to retrieve the number of types from RapidJSON so they're hard-coded here.
@@ -87,6 +95,7 @@ namespace JsonSerializationTests
{
public:
using Type = T;
virtual ~JsonSerializerConformityTestDescriptor() = default;
virtual AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() = 0;
@@ -104,13 +113,21 @@ namespace JsonSerializationTests
virtual AZStd::shared_ptr<T> CreatePartialDefaultInstance() { return nullptr; }
//! Create an instance where all values are set to non-default values.
virtual AZStd::shared_ptr<T> CreateFullySetInstance() = 0;
//! Create an instance of the target array type with a single value that has all defaults.
//! If the target type doesn't support arrays or requires more than one entry this can be ignored and
//! tests using this value will be skipped.
virtual AZStd::shared_ptr<T> CreateSingleArrayDefaultInstance() { return nullptr; }
//! Get the json that represents the default instance.
//! If the target type doesn't support partial specialization this can be ignored and
//! tests for partial support will be skipped.
virtual AZStd::string_view GetJsonForPartialDefaultInstance() { return ""; }
virtual AZStd::string_view GetJsonForPartialDefaultInstance() { return ""; }
//! Get the json that represents the instance with all values set.
virtual AZStd::string_view GetJsonForFullySetInstance() = 0;
//! Get the json that represents an array with a single value that has only defaults.
//! If the target type doesn't support arrays or requires more than one entry this can be ignored and
//! tests using this value will be skipped.
virtual AZStd::string_view GetJsonForSingleArrayDefaultInstance() { return "[{}]"; }
//! Get the json where additional values are added to the json file.
//! If this function is not overloaded, but features.m_supportsInjection is enabled then
//! the Json Serializer Conformity Tests will inject extra values in the json for a fully.
@@ -138,12 +155,15 @@ namespace JsonSerializationTests
virtual AZStd::string_view GetJsonFor_Load_DeserializeUnreflectedType() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializeFullySetInstance() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializePartialInstance() { return this->GetJsonForPartialDefaultInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializeArrayWithDefaultValue() { return this->GetJsonForSingleArrayDefaultInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializeFullInstanceOnTopOfPartialDefaulted() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Load_HaltedThroughCallback() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeWithDefaultsKept() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeFullySetInstance() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeWithoutDefault() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeWithoutDefaultAndDefaultsKept() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializePartialInstance() { return this->GetJsonForPartialDefaultInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeArrayWithSingleDefaultValue() { return this->GetJsonForSingleArrayDefaultInstance(); }
};
template<typename T>
@@ -154,6 +174,21 @@ namespace JsonSerializationTests
using Description = T;
using Type = typename T::Type;
struct PointerWrapper
{
AZ_TYPE_INFO(PointerWrapper, "{32FA6645-074A-458A-B79C-B173D0BD4B42}");
AZ_CLASS_ALLOCATOR(PointerWrapper, AZ::SystemAllocator, 0);
Type* m_value{ nullptr };
~PointerWrapper()
{
// Using free because not all types can safely use delete. Since this just to clear the memory to satisfy the memory
// leak test, this is fine.
azfree(m_value);
}
};
void SetUp() override
{
using namespace AZ::JsonSerializationResult;
@@ -165,6 +200,7 @@ namespace JsonSerializationTests
descriptor->ConfigureFeatures(this->m_features);
descriptor->Reflect(this->m_serializeContext);
descriptor->Reflect(this->m_jsonRegistrationContext);
this->m_serializeContext->template Class<PointerWrapper>()->Field("Value", &PointerWrapper::m_value);
this->m_deserializationSettings->m_reporting = &Internal::VerifyCallback;
this->m_serializationSettings->m_reporting = &Internal::VerifyCallback;
@@ -185,6 +221,7 @@ namespace JsonSerializationTests
this->m_jsonRegistrationContext->DisableRemoveReflection();
this->m_serializeContext->EnableRemoveReflection();
this->m_serializeContext->template Class<PointerWrapper>()->Field("Value", &PointerWrapper::m_value);
descriptor->Reflect(this->m_serializeContext);
this->m_serializeContext->DisableRemoveReflection();
@@ -487,6 +524,41 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeArrayWithDefaultValue_SucceedsAndReportPartialDefaults)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.SupportsJsonType(rapidjson::kArrayType))
{
this->m_jsonDocument->Parse(this->m_description.GetJsonFor_Load_DeserializeArrayWithDefaultValue().data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
this->m_jsonDeserializationContext->PushPath(DefaultPath);
ResultCode result =
serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
if (this->m_features.m_fixedSizeArray)
{
EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome());
EXPECT_EQ(Processing::Altered, result.GetProcessing());
}
else
{
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
auto compare = this->m_description.CreateSingleArrayDefaultInstance();
ASSERT_NE(nullptr, compare)
<< "Conformity tests for variably sized arrays require an implementation of CreateSingleArrayDefaultInstance";
EXPECT_TRUE(this->m_description.AreEqual(*compare, *instance));
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_InterruptClearingTarget_ContainerIsNotCleared)
{
using namespace AZ::JsonSerializationResult;
@@ -548,7 +620,6 @@ namespace JsonSerializationTests
this->m_jsonDocument->Parse(json.data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto compare = this->m_description.CreateFullySetInstance();
@@ -622,6 +693,94 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeFullInstanceOnTopOfPartialDefaulted_SucceedsAndObjectMatchesParialInstance)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_supportsPartialInitialization)
{
AZStd::string_view json = this->m_description.GetJsonFor_Load_DeserializeFullInstanceOnTopOfPartialDefaulted();
// If tests for partial initialization are enabled than json for the partial initialization is needed.
ASSERT_FALSE(json.empty());
this->m_jsonDocument->Parse(json.data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreatePartialDefaultInstance();
auto compare = this->m_description.CreateFullySetInstance();
ASSERT_NE(nullptr, compare);
// Clear containers which should effectively turn them into default containers.
this->m_deserializationSettings->m_clearContainers = true;
this->ResetJsonContexts();
this->m_jsonDeserializationContext->PushPath(DefaultPath);
ResultCode result =
serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
EXPECT_EQ(Outcomes::Success, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare));
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DefaultToPointer_SucceedsAndValueIsInitialized)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_enableNewInstanceTests && this->m_features.m_mandatoryFields.empty())
{
AZ::SerializeContext* serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(azrtti_typeid<typename TypeParam::Type>());
ASSERT_NE(nullptr, classData);
// Skip this test if the target type doesn't have a factor to create a new instance with or if the factor explicit
// prohibits construction.
if (classData->m_factory && classData->m_factory != AZ::Internal::NullFactory::GetInstance())
{
typename JsonSerializerConformityTests<TypeParam>::PointerWrapper instance;
auto compare = this->m_description.CreateDefaultInstance();
this->m_jsonDocument->Parse(R"({ "Value": {}})");
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
AZ::JsonDeserializerSettings settings;
settings.m_serializeContext = serializeContext;
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
ResultCode result = AZ::JsonSerialization::Load(instance, *this->m_jsonDocument, settings);
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, instance.m_value);
EXPECT_TRUE(this->m_description.AreEqual(*instance.m_value, *compare));
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_InitializeNewInstance_SucceedsAndValueIsInitialized)
{
using namespace AZ;
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_enableNewInstanceTests && this->m_features.m_mandatoryFields.empty())
{
auto serializer = this->m_description.CreateSerializer();
if ((serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::InitializeNewInstance) ==
BaseJsonSerializer::OperationFlags::InitializeNewInstance)
{
typename TypeParam::Type instance;
auto compare = this->m_description.CreateDefaultInstance();
this->m_jsonDocument->SetObject();
ResultCode result =
serializer->Load(&instance, azrtti_typeid(instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_TRUE(this->m_description.AreEqual(instance, *compare));
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_HaltedThroughCallback_LoadFailsAndHaltReported)
{
using namespace AZ::JsonSerializationResult;
@@ -909,6 +1068,28 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeArrayWithSingleDefaultValue_StoredSuccessfullyAndJsonMatches)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.SupportsJsonType(rapidjson::kArrayType) && !this->m_features.m_fixedSizeArray)
{
this->m_jsonSerializationContext->PushPath(DefaultPath);
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateSingleArrayDefaultInstance();
ASSERT_NE(nullptr, instance)
<< "Conformity tests for variably sized arrays require an implementation of CreateSingleArrayDefaultInstance";
ResultCode result = serializer->Store(
*this->m_jsonDocument, instance.get(), instance.get(), azrtti_typeid(*instance), *this->m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
this->Expect_DocStrEq(this->m_description.GetJsonFor_Store_SerializeArrayWithSingleDefaultValue());
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Store_HaltedThroughCallback_StoreFailsAndHaltReported)
{
using namespace AZ::JsonSerializationResult;
@@ -1017,7 +1198,29 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared)
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationFlags_RequiresExplicitInit_ObjectsThatDoNotConstructHaveExplicitInitOption)
{
using namespace AZ;
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_enableInitializationTest)
{
auto instance = this->m_description.CreateDefaultInstance();
typename TypeParam::Type compare;
if (!this->m_description.AreEqual(*instance, compare))
{
auto serializer = this->m_description.CreateSerializer();
BaseJsonSerializer::OperationFlags flags = serializer->GetOperationsFlags();
bool hasManualDefaultSet =
(flags & BaseJsonSerializer::OperationFlags::ManualDefault) == BaseJsonSerializer::OperationFlags::ManualDefault ||
(flags & BaseJsonSerializer::OperationFlags::InitializeNewInstance) ==
BaseJsonSerializer::OperationFlags::InitializeNewInstance;
EXPECT_TRUE(hasManualDefaultSet);
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared)
{
if (this->m_features.SupportsJsonType(rapidjson::kObjectType))
{
@@ -1048,10 +1251,14 @@ namespace JsonSerializationTests
Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArrayWithClearEnabled_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArrayWithClearedTarget_SucceedsAndObjectMatchesDefaults,
Load_DeserializeArrayWithDefaultValue_SucceedsAndReportPartialDefaults,
Load_InterruptClearingTarget_ContainerIsNotCleared,
Load_DeserializeFullySetInstance_SucceedsAndObjectMatchesFullySetInstance,
Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance,
Load_DeserializePartialInstance_SucceedsAndObjectMatchesParialInstance,
Load_DeserializeFullInstanceOnTopOfPartialDefaulted_SucceedsAndObjectMatchesParialInstance,
Load_DefaultToPointer_SucceedsAndValueIsInitialized,
Load_InitializeNewInstance_SucceedsAndValueIsInitialized,
Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported,
Load_InsertAdditionalData_SucceedsAndObjectMatchesFullySetInstance,
Load_HaltedThroughCallback_LoadFailsAndHaltReported,
@@ -1066,13 +1273,15 @@ namespace JsonSerializationTests
Store_SerializeWithoutDefaultAndDefaultsKept_StoredSuccessfullyAndJsonMatches,
Store_SerializePartialInstance_StoredSuccessfullyAndJsonMatches,
Store_SerializeEmptyArray_StoredSuccessfullyAndJsonMatches,
Store_SerializeArrayWithSingleDefaultValue_StoredSuccessfullyAndJsonMatches,
Store_HaltedThroughCallback_StoreFailsAndHaltReported,
StoreLoad_RoundTripWithPartialDefault_IdenticalInstances,
StoreLoad_RoundTripWithFullSet_IdenticalInstances,
StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances,
GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared);
GetOperationFlags_RequiresExplicitInit_ObjectsThatDoNotConstructHaveExplicitInitOption,
GetOperationFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared);
} // namespace JsonSerializationTests
namespace AZ
@@ -35,6 +35,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<Map>();
}
AZStd::string_view GetJsonForSingleArrayDefaultInstance() override
{
return R"({ "{}": {} })";
}
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
{
features.EnableJsonType(rapidjson::kArrayType);
@@ -61,6 +66,13 @@ namespace JsonSerializationTests
public:
using Map = T<int, double>;
AZStd::shared_ptr<Map> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::make_shared<Map>();
instance->emplace(AZStd::make_pair(0, 0.0));
return instance;
}
AZStd::shared_ptr<Map> CreateFullySetInstance() override
{
auto instance = AZStd::make_shared<Map>();
@@ -100,6 +112,13 @@ namespace JsonSerializationTests
public:
using Map = T<AZStd::string, double>;
AZStd::shared_ptr<Map> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::make_shared<Map>();
instance->emplace(AZStd::make_pair(AZStd::string(), 0.0));
return instance;
}
AZStd::shared_ptr<Map> CreateFullySetInstance() override
{
auto instance = AZStd::make_shared<Map>();
@@ -163,6 +182,14 @@ namespace JsonSerializationTests
return instance;
}
AZStd::shared_ptr<Map> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::shared_ptr<Map>(new Map{}, &Delete);
instance->emplace(AZStd::make_pair(aznew SimpleClass(), aznew SimpleClass()));
return instance;
}
AZStd::string_view GetJsonForPartialDefaultInstance() override
{
if constexpr (IsMultiMap)
@@ -237,13 +264,23 @@ namespace JsonSerializationTests
return false;
}
auto compare = [](typename Map::const_reference lhs, typename Map::const_reference rhs) -> bool
// Naive compare to avoid having to split up the test because comparing for ordered and unordered maps would need to be
// different.
for (auto&& [key, value] : lhs)
{
return
lhs.first->Equals(*rhs.first, true) &&
lhs.second->Equals(*rhs.second, true);
};
return AZStd::equal(lhs.begin(), lhs.end(), rhs.begin(), compare);
for (auto&& [keyCompare, valueCompare] : rhs)
{
if (key->Equals(*keyCompare, true))
{
if (!value->Equals(*valueCompare, true))
{
return false;
}
break;
}
}
}
return true;
}
};
@@ -393,6 +430,29 @@ namespace JsonSerializationTests
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"{}": {}
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
TestStringMap values;
ResultCode result = m_unorderedMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
EXPECT_EQ(1, values.size());
auto defaultKey = values.find(TestString());
EXPECT_NE(values.end(), defaultKey);
EXPECT_STRCASEEQ(TestString().m_value.c_str(), defaultKey->second.m_value.c_str());
}
TEST_F(JsonMapSerializerTests, Load_DefaultForStringKeyAndAdditionalValue_LoadedBackWithDefaults)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"{}": {},
@@ -563,13 +623,13 @@ namespace JsonSerializationTests
EXPECT_EQ(Outcomes::Catastrophic, result.GetOutcome());
}
TEST_F(JsonMapSerializerTests, Load_DefaultValueInMultiMap_DefaultUsed)
TEST_F(JsonMapSerializerTests, Load_DefaultObjectInMultiMap_DefaultUsed)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"Hello": {}
"World": {}
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
@@ -581,17 +641,40 @@ namespace JsonSerializationTests
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
ASSERT_FALSE(values.empty());
EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str());
EXPECT_STREQ("World", values.begin()->first.m_value.c_str());
EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str());
}
TEST_F(JsonMapSerializerTests, Load_DefaultArrayValueInMultiMap_DefaultUsed)
TEST_F(JsonMapSerializerTests, Load_FullDefaultObjectInMultiMap_DefaultUsed)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"Hello": [{}]
"{}": {}
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
TestStringMultiMap values;
ResultCode result =
m_unorderedMultiMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
ASSERT_FALSE(values.empty());
EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str());
EXPECT_STREQ("Hello", values.begin()->second.m_value.c_str());
EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str());
}
TEST_F(JsonMapSerializerTests, Load_DefaultObjectValueInMultiMap_DefaultUsed)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"World": [{}]
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
@@ -603,7 +686,8 @@ namespace JsonSerializationTests
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
ASSERT_FALSE(values.empty());
EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str());
EXPECT_STREQ("World", values.begin()->first.m_value.c_str());
EXPECT_STREQ("Hello", values.begin()->second.m_value.c_str());
EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str());
}
@@ -636,7 +720,7 @@ namespace JsonSerializationTests
azrtti_typeid(&values), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq(R"(
{
"{}": {}
@@ -654,7 +738,25 @@ namespace JsonSerializationTests
azrtti_typeid(&values), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq(R"(
{
"{}": {}
})");
}
TEST_F(JsonMapSerializerTests, Store_SingleAllDefaulValue_InitializedWithDefaults)
{
using namespace AZ::JsonSerializationResult;
SimpleClassMap values;
values.emplace(SimpleClass(), SimpleClass());
ResultCode result =
m_unorderedMapSerializer.Store(*m_jsonDocument, &values, nullptr, azrtti_typeid(&values), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq(R"(
{
"{}": {}
@@ -303,6 +303,29 @@ namespace JsonSerializationTests
R"( { "foo": [ "bar", "baz" ] })");
}
TEST_F(JsonPatchingSerializationTests, ApplyPatch_UseJsonPatchRemoveArrayMembersInCorrectOrder_ReportsSuccess)
{
CheckApplyPatch(
R"( { "foo": [ "bar", "qux", "baz" ] })",
R"( [
{ "op": "remove", "path": "/foo/2" },
{ "op": "remove", "path": "/foo/1" }
])",
R"( { "foo": [ "bar" ] })");
}
TEST_F(JsonPatchingSerializationTests, ApplyPatch_UseJsonPatchRemoveArrayMembersInWrongOrder_ReportsError)
{
using namespace AZ::JsonSerializationResult;
CheckApplyPatchOutcome(
R"( { "foo": [ "bar", "qux", "baz" ] })",
R"( [
{ "op": "remove", "path": "/foo/1" },
{ "op": "remove", "path": "/foo/2" }
])",
Outcomes::Invalid, Processing::Halted);
}
TEST_F(JsonPatchingSerializationTests, ApplyPatch_UseJsonPatchRemoveOperationInvalidParent_ReportError)
{
using namespace AZ::JsonSerializationResult;
@@ -949,6 +972,27 @@ namespace JsonSerializationTests
);
}
TEST_F(JsonPatchingSerializationTests, CreatePatch_UseJsonPatchRemoveLastArrayEntries_MultipleOperationsInCorrectOrder)
{
CheckCreatePatch(
R"( [ "foo", "hello", "bar" ])", R"( [ "foo" ])",
R"( [
{ "op": "remove", "path": "/2" },
{ "op": "remove", "path": "/1" }
])");
}
TEST_F(JsonPatchingSerializationTests, CreatePatch_UseJsonPatchRemoveAllArrayEntries_MultipleOperationsInCorrectOrder)
{
CheckCreatePatch(
R"( [ "foo", "hello", "bar" ])", R"( [])",
R"( [
{ "op": "remove", "path": "/2" },
{ "op": "remove", "path": "/1" },
{ "op": "remove", "path": "/0" }
])");
}
TEST_F(JsonPatchingSerializationTests, CreatePatch_UseJsonPatchRemoveObjectFromArrayInMiddle_OperationToUpdateMember)
{
CheckCreatePatch(
@@ -48,12 +48,12 @@ namespace JsonSerializationTests
AZStd::shared_ptr<Pair> CreateDefaultInstance() override
{
return AZStd::make_shared<Pair>(142, 242.0);
return AZStd::make_shared<Pair>(0, 0.0);
}
AZStd::shared_ptr<Pair> CreatePartialDefaultInstance() override
{
return AZStd::make_shared<Pair>(142, 288.0);
return AZStd::make_shared<Pair>(0, 288.0);
}
AZStd::shared_ptr<Pair> CreateFullySetInstance() override
@@ -102,12 +102,12 @@ namespace JsonSerializationTests
AZStd::shared_ptr<Tuple> CreateDefaultInstance() override
{
return AZStd::make_shared<Tuple>(142, 242.0, 342.0f);
return AZStd::make_shared<Tuple>(0, 0.0, 0.0f);
}
AZStd::shared_ptr<Tuple> CreatePartialDefaultInstance() override
{
return AZStd::make_shared<Tuple>(142, 288.0, 342.0f);
return AZStd::make_shared<Tuple>(0, 288.0, 0.0f);
}
AZStd::shared_ptr<Tuple> CreateFullySetInstance() override
@@ -345,6 +345,7 @@ namespace JsonSerializationTests
{
TupleSerializerTestsInternal::ConfigureFeatures(features);
features.m_supportsPartialInitialization = true;
features.m_enableNewInstanceTests = false;
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
@@ -447,14 +448,14 @@ namespace JsonSerializationTests
{
return AZStd::make_shared<Tuple>(
AZStd::vector<int>(),
AZStd::make_pair(442, ""));
AZStd::make_pair(0, ""));
}
AZStd::shared_ptr<Tuple> CreatePartialDefaultInstance() override
{
return AZStd::make_shared<Tuple>(
AZStd::vector<int>(),
AZStd::make_pair(442, "hello"));
AZStd::make_pair(0, "hello"));
}
AZStd::shared_ptr<Tuple> CreateFullySetInstance() override
@@ -36,6 +36,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<Set>();
}
AZStd::shared_ptr<Set> CreateSingleArrayDefaultInstance() override
{
return AZStd::make_shared<Set>(Set{ 0 });
}
AZStd::shared_ptr<Set> CreateFullySetInstance() override
{
return AZStd::make_shared<Set>(Set{42, -88, 342});
@@ -80,6 +85,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<MultiSet>();
}
AZStd::shared_ptr<MultiSet> CreateSingleArrayDefaultInstance() override
{
return AZStd::make_shared<MultiSet>(MultiSet{ 0 });
}
AZStd::shared_ptr<MultiSet> CreateFullySetInstance() override
{
return AZStd::make_shared<MultiSet>(MultiSet{ 42, -88, 42, 342 });
@@ -48,7 +48,8 @@ namespace AzPhysics
using OnPresimulateEvent = AZ::Event<float>;
//! Event triggers at the end of the SystemInterface::Simulate call.
using OnPostsimulateEvent = AZ::Event<>;
//! Parameter is the total time that the physics system will run for during the Simulate call.
using OnPostsimulateEvent = AZ::Event<float>;
//! Event trigger when a Scene is added to the simulation.
//! When triggered will send the handle to the new Scene.
@@ -402,7 +402,7 @@ namespace Physics
->Attribute(AZ_CRC_CE("EditButton"), "")
->Attribute(AZ_CRC_CE("EditDescription"), "Open in Asset Editor")
->Attribute(AZ::Edit::Attributes::DefaultAsset, &MaterialSelection::GetMaterialLibraryId)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "", "")
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Slots", "")
->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryId)
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
@@ -43,7 +43,7 @@ namespace AzPhysics
const AZ::BehaviorAzEventDescription postsimulateEventDescription =
{
"Postsimulate event",
{} // Parameters
{"Tick time"} // Parameters
};
behaviorContext->Class<SystemInterface>("System Interface")
@@ -1,47 +1,36 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/EBus/EBus.h>
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace AzToolsFramework
{
using EntityIdList = AZStd::vector<AZ::EntityId>;
/*!
* Bus for notifications about entity transform changes from the editor viewport
*/
class EditorTransformChangeNotifications
: public AZ::EBusTraits
//! Notifications about entity transform changes from the editor.
class EditorTransformChangeNotifications : public AZ::EBusTraits
{
public:
virtual ~EditorTransformChangeNotifications() = default;
//! A notification that these entities had their transforms changed due to a user interaction in the editor.
//! @param entityIds Entities that had their transform changed.
virtual void OnEntityTransformChanged([[maybe_unused]] const AzToolsFramework::EntityIdList& entityIds)
{
}
/*!
* Notification that the specified entities are about to have their transforms changed due to user interaction in the editor viewport
*
* \param entityIds Entities about to be changed
*/
virtual void OnEntityTransformChanging(const AzToolsFramework::EntityIdList& /*entityIds*/) {};
/*!
* Notification that the specified entities had their transforms changed due to user interaction in the editor viewport
*
* \param entityIds Entities changed
*/
virtual void OnEntityTransformChanged(const AzToolsFramework::EntityIdList& /*entityIds*/) {};
protected:
~EditorTransformChangeNotifications() = default;
};
using EditorTransformChangeNotificationBus = AZ::EBus<EditorTransformChangeNotifications>;
} // namespace AzToolsFramework
@@ -27,6 +27,7 @@
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/Entity/EditorEntityTransformBus.h>
#include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
@@ -944,7 +945,7 @@ namespace AzToolsFramework
return AZ::Success();
}
AZ::u32 TransformComponent::ParentChanged()
AZ::u32 TransformComponent::ParentChangedInspector()
{
AZ::u32 refreshLevel = AZ::Edit::PropertyRefreshLevels::None;
@@ -974,12 +975,23 @@ namespace AzToolsFramework
return refreshLevel;
}
AZ::u32 TransformComponent::TransformChanged()
AZ::u32 TransformComponent::TransformChangedInspector()
{
if (TransformChanged())
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
EntityIdList{ GetEntityId() });
}
return AZ::Edit::PropertyRefreshLevels::None;
}
bool TransformComponent::TransformChanged()
{
if (!m_suppressTransformChangedEvent)
{
auto parent = GetParentTransformComponent();
if (parent)
if (auto parent = GetParentTransformComponent())
{
OnTransformChanged(parent->GetLocalTM(), parent->GetWorldTM());
}
@@ -987,13 +999,15 @@ namespace AzToolsFramework
{
OnTransformChanged(AZ::Transform::Identity(), AZ::Transform::Identity());
}
return true;
}
return AZ::Edit::PropertyRefreshLevels::None;
return false;
}
// This is called when our transform changes static state.
AZ::u32 TransformComponent::StaticChanged()
AZ::u32 TransformComponent::StaticChangedInspector()
{
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay,
@@ -1175,10 +1189,10 @@ namespace AzToolsFramework
Attribute(AZ::Edit::Attributes::AutoExpand, true)->
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_parentEntityId, "Parent entity", "")->
Attribute(AZ::Edit::Attributes::ChangeValidate, &TransformComponent::ValidatePotentialParent)->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::ParentChanged)->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::ParentChangedInspector)->
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::DontGatherReference | AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_editorTransform, "Values", "")->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::TransformChanged)->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::TransformChangedInspector)->
Attribute(AZ::Edit::Attributes::AutoExpand, true)->
DataElement(AZ::Edit::UIHandlers::Button, &TransformComponent::m_addNonUniformScaleButton, "", "")->
Attribute(AZ::Edit::Attributes::ButtonText, "Add non-uniform scale")->
@@ -1189,7 +1203,7 @@ namespace AzToolsFramework
EnumAttribute(AZ::TransformConfig::ParentActivationTransformMode::MaintainOriginalRelativeTransform, "Original relative transform")->
EnumAttribute(AZ::TransformConfig::ParentActivationTransformMode::MaintainCurrentWorldTransform, "Current world transform")->
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_isStatic ,"Static", "Static entities are highly optimized and cannot be moved during runtime.")->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::StaticChanged)->
Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::StaticChangedInspector)->
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_cachedWorldTransformParent, "Cached Parent Entity", "")->
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::DontGatherReference | AZ::Edit::SliceFlags::NotPushable)->
Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)->
@@ -182,9 +182,12 @@ namespace AzToolsFramework
static void Reflect(AZ::ReflectContext* context);
AZ::Outcome<void, AZStd::string> ValidatePotentialParent(void* newValue, const AZ::Uuid& valueType);
AZ::u32 ParentChanged();
AZ::u32 TransformChanged();
AZ::u32 StaticChanged();
AZ::u32 TransformChangedInspector();
AZ::u32 ParentChangedInspector();
AZ::u32 StaticChangedInspector();
bool TransformChanged();
AZ::Transform GetLocalTranslationTM() const;
AZ::Transform GetLocalRotationTM() const;
@@ -165,11 +165,13 @@ namespace UnitTest
void EditorEntityComponentChangeDetector::OnEntityTransformChanged(
const AzToolsFramework::EntityIdList& entityIds)
{
m_entityIds = entityIds;
for (const AZ::EntityId& entityId : entityIds)
{
if (const auto* entity = GetEntityById(entityId))
{
if (AZ::Component * transformComponent = entity->FindComponent<Components::TransformComponent>())
if (AZ::Component* transformComponent = entity->FindComponent<Components::TransformComponent>())
{
OnEntityComponentPropertyChanged(transformComponent->GetId());
}
@@ -239,6 +239,7 @@ namespace UnitTest
bool PropertyDisplayInvalidated() const { return m_propertyDisplayInvalidated; }
AZStd::vector<AZ::ComponentId> m_componentIds;
AzToolsFramework::EntityIdList m_entityIds;
private:
// PropertyEditorEntityChangeNotificationBus ...
@@ -1024,7 +1024,6 @@ namespace AzToolsFramework
static void RefreshUiAfterChange(const EntityIdList& entitiyIds)
{
EditorTransformChangeNotificationBus::Broadcast(&EditorTransformChangeNotifications::OnEntityTransformChanged, entitiyIds);
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
}
@@ -1097,7 +1096,7 @@ namespace AzToolsFramework
auto entityBoxSelectData = AZStd::make_shared<EntityBoxSelectData>();
m_boxSelect.InstallLeftMouseDown(
[this, entityBoxSelectData](const ViewportInteraction::MouseInteractionEvent& /*mouseInteraction*/)
[this, entityBoxSelectData]([[maybe_unused]] const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
// begin selection undo/redo command
entityBoxSelectData->m_boxSelectSelectionCommand =
@@ -1295,8 +1294,12 @@ namespace AzToolsFramework
});
translationManipulators->InstallLinearManipulatorMouseUpCallback(
[this]([[maybe_unused]] const LinearManipulator::Action& action) mutable
[this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) mutable
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
manipulatorEntityIds->m_entityIds);
EndRecordManipulatorCommand();
});
@@ -1325,8 +1328,12 @@ namespace AzToolsFramework
});
translationManipulators->InstallPlanarManipulatorMouseUpCallback(
[this, manipulatorEntityIds](const PlanarManipulator::Action& /*action*/)
[this, manipulatorEntityIds]([[maybe_unused]] const PlanarManipulator::Action& action)
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
manipulatorEntityIds->m_entityIds);
EndRecordManipulatorCommand();
});
@@ -1354,8 +1361,12 @@ namespace AzToolsFramework
});
translationManipulators->InstallSurfaceManipulatorMouseUpCallback(
[this, manipulatorEntityIds](const SurfaceManipulator::Action& /*action*/)
[this, manipulatorEntityIds]([[maybe_unused]] const SurfaceManipulator::Action& action)
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
manipulatorEntityIds->m_entityIds);
EndRecordManipulatorCommand();
});
@@ -1392,7 +1403,7 @@ namespace AzToolsFramework
AZStd::shared_ptr<SharedRotationState> sharedRotationState = AZStd::make_shared<SharedRotationState>();
rotationManipulators->InstallLeftMouseDownCallback(
[this, sharedRotationState](const AngularManipulator::Action& /*action*/) mutable -> void
[this, sharedRotationState]([[maybe_unused]] const AngularManipulator::Action& action) mutable -> void
{
sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
sharedRotationState->m_referenceFrameAtMouseDown = m_referenceFrame;
@@ -1518,8 +1529,12 @@ namespace AzToolsFramework
});
rotationManipulators->InstallLeftMouseUpCallback(
[this](const AngularManipulator::Action& /*action*/)
[this, sharedRotationState]([[maybe_unused]] const AngularManipulator::Action& action)
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
sharedRotationState->m_entityIds);
EndRecordManipulatorCommand();
});
@@ -1565,6 +1580,10 @@ namespace AzToolsFramework
auto uniformLeftMouseUpCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action)
{
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged,
manipulatorEntityIds->m_entityIds);
m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform(
m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame));
};
@@ -2402,7 +2421,7 @@ namespace AzToolsFramework
AddAction(
m_actions, { QKeySequence(Qt::Key_U) },
/*ID_VIEWPORTUI_VISIBLE=*/50040, "Toggle ViewportUI", "Hide/Unhide Viewport UI",
/*ID_VIEWPORTUI_VISIBLE=*/50040, "Toggle Viewport UI", "Hide/Show Viewport UI",
[this]()
{
SetAllViewportUiVisible(!m_viewportUiVisible);
@@ -3642,17 +3661,17 @@ namespace AzToolsFramework
RegenerateManipulators();
}
void EditorTransformComponentSelection::OnEntityVisibilityChanged(const bool /*visibility*/)
void EditorTransformComponentSelection::OnEntityVisibilityChanged([[maybe_unused]] const bool visibility)
{
m_selectedEntityIdsAndManipulatorsDirty = true;
}
void EditorTransformComponentSelection::OnEntityLockChanged(const bool /*locked*/)
void EditorTransformComponentSelection::OnEntityLockChanged([[maybe_unused]] const bool locked)
{
m_selectedEntityIdsAndManipulatorsDirty = true;
}
void EditorTransformComponentSelection::EnteredComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
void EditorTransformComponentSelection::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
SetAllViewportUiVisible(false);
@@ -3661,7 +3680,7 @@ namespace AzToolsFramework
ToolsApplicationNotificationBus::Handler::BusDisconnect();
}
void EditorTransformComponentSelection::LeftComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
void EditorTransformComponentSelection::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
SetAllViewportUiVisible(true);
@@ -85,6 +85,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
AZ::AzManipulatorTestFramework.Static
AZ::AzTest
AZ::AzQtComponents
RUNTIME_DEPENDENCIES
3rdParty::Qt::Test
)
ly_add_googletest(
NAME AZ::AzToolsFramework.Tests
@@ -425,6 +425,43 @@ namespace UnitTest
EXPECT_TRUE(finalEntityTransform.IsClose(finalTransformWorld, 0.01f));
}
TEST_F(EditorTransformComponentSelectionManipulatorTestFixture, TranslatingEntityWithLinearManipulatorNotifiesOnEntityTransformChanged)
{
EditorEntityComponentChangeDetector editorEntityChangeDetector(m_entity1);
// the initial starting position of the entity (in front and to the left of the camera)
const auto initialTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, 10.0f, 0.0f));
// where the entity should end up (in front and to the right of the camera)
const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f));
// calculate the position in screen space of the initial position of the entity
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
// calculate the position in screen space of the final position of the entity
const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
// move the entity to its starting position
AzToolsFramework::SetWorldTransform(m_entity1, initialTransformWorld);
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
AzToolsFramework::SelectEntity(m_entity1);
// create an offset along the linear manipulator pointing along the x-axis (perpendicular to the camera view)
const auto mouseOffsetOnManipulator = AzFramework::ScreenVector(10, 0);
// store the mouse down position on the manipulator
const auto mouseDownPosition = initialPositionScreen + mouseOffsetOnManipulator;
// final position in screen space of the mouse
const auto mouseMovePosition = finalPositionScreen + mouseOffsetOnManipulator;
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(mouseDownPosition)
->MouseLButtonDown()
->MousePosition(mouseMovePosition)
->MouseLButtonUp();
// verify a EditorTransformChangeNotificationBus::OnEntityTransformChanged occurred
using ::testing::UnorderedElementsAreArray;
EXPECT_THAT(editorEntityChangeDetector.m_entityIds, UnorderedElementsAreArray(m_entityIds));
}
// simple widget to listen for a mouse wheel event and then forward it on to the ViewportSelectionRequestBus
class WheelEventWidget : public QWidget
{
@@ -29,15 +29,20 @@ namespace Benchmark
{},
m_pathString));
auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(instance->GetTemplateId());
for (auto _ : state)
{
state.PauseTiming();
AzFramework::Spawnable spawnable;
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom);
state.ResumeTiming();
// Create a vector to store spawnables so that they don't get destroyed immediately after construction.
AZStd::vector<AZStd::unique_ptr<AzFramework::Spawnable>> spawnables;
spawnables.reserve(numSpawnables);
for (int spwanableCounter = 0; spwanableCounter < numSpawnables; ++spwanableCounter)
{
AZStd::unique_ptr<AzFramework::Spawnable> spawnable = AZStd::make_unique<AzFramework::Spawnable>();
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(*spawnable, prefabDom);
spawnables.push_back(AZStd::move(spawnable));
}
}
state.SetComplexityN(numSpawnables);
@@ -50,3 +55,4 @@ namespace Benchmark
}
#endif
+37 -35
View File
@@ -9,7 +9,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
@@ -27,40 +27,42 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AzFramework
)
ly_add_target(
NAME ProcessLaunchTest EXECUTABLE
NAMESPACE AZ
FILES_CMAKE
process_launch_test_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME ProcessLaunchTest EXECUTABLE
NAMESPACE AZ
FILES_CMAKE
process_launch_test_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
)
ly_add_target(
NAME Framework.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
FILES_CMAKE
frameworktests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::AzToolsFramework
AZ::AzTestShared
AZ::AzFrameworkTestShared
RUNTIME_DEPENDENCIES
AZ::ProcessLaunchTest
)
ly_add_googletest(
NAME AZ::Framework.Tests
)
ly_add_target(
NAME Framework.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
FILES_CMAKE
frameworktests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::AzToolsFramework
AZ::AzTestShared
AZ::AzFrameworkTestShared
RUNTIME_DEPENDENCIES
AZ::ProcessLaunchTest
)
ly_add_googletest(
NAME AZ::Framework.Tests
)
endif()
endif()
@@ -0,0 +1,84 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/UnitTest/UnitTest.h>
#include <gmock/gmock.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <AzFramework/Spawnable/SpawnableEntitiesManager.h>
namespace AzFramework
{
class MockSpawnableEntitiesInterface;
using NiceSpawnableEntitiesInterfaceMock = ::testing::NiceMock<MockSpawnableEntitiesInterface>;
class MockSpawnableEntitiesInterface : public SpawnableEntitiesDefinition
{
public:
AZ_RTTI(MockSpawnableEntitiesInterface, "{2A20FF73-C445-4F32-ABB9-5CF0A5778404}", SpawnableEntitiesDefinition);
MockSpawnableEntitiesInterface()
{
AZ::Interface<SpawnableEntitiesDefinition>::Register(this);
}
virtual ~MockSpawnableEntitiesInterface()
{
AZ::Interface<SpawnableEntitiesDefinition>::Unregister(this);
}
MOCK_METHOD2(SpawnAllEntities, void(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(
SpawnEntities,
void(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs));
MOCK_METHOD2(DespawnAllEntities, void(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(
ReloadSpawnable,
void(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs));
MOCK_METHOD3(
ListEntities, void(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(
ListIndicesAndEntities,
void(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(
ClaimEntities,
void(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(Barrier, void(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs));
MOCK_METHOD1(CreateTicket, AZStd::pair<EntitySpawnTicket::Id, void*>(AZ::Data::Asset<Spawnable>&& spawnable));
MOCK_METHOD1(DestroyTicket, void(void* ticket));
/** Installs some default result values for the above functions.
* Note that you can always override these in scope of your test by adding additional ON_CALL / EXPECT_CALL
* statements in the body of your test or after calling this function, and yours will take precedence.
**/
static void InstallDefaultReturns(NiceSpawnableEntitiesInterfaceMock& target)
{
using namespace ::testing;
// The ID and pointer are completely arbitrary, they just need to both be non-zero to look like a valid ticket.
constexpr EntitySpawnTicket::Id ticketId(1);
static int ticketPayload = 0;
ON_CALL(target, CreateTicket(_)).WillByDefault(
Return(AZStd::make_pair<AzFramework::EntitySpawnTicket::Id, void*>(ticketId, &ticketPayload)));
}
};
} // namespace AzFramework
@@ -10,6 +10,7 @@
#
set(FILES
Mocks/MockSpawnableEntitiesInterface.h
Utils/Utils.h
Utils/Utils.cpp
)