@ -13,7 +13,7 @@
# include <AzCore/IO/FileReader.h>
# include <AzCore/IO/Path/Path.h>
# include <AzCore/JSON/error/en.h>
# include <AzCore/NativeUI/ / NativeUIRequests.h>
# include <AzCore/NativeUI/ NativeUIRequests.h>
# include <AzCore/Serialization/Json/JsonSerialization.h>
# include <AzCore/Serialization/Json/StackedString.h>
# include <AzCore/Settings/SettingsRegistryImpl.h>
@ -21,6 +21,34 @@
# include <AzCore/std/sort.h>
# include <AzCore/std/parallel/scoped_lock.h>
namespace AZ : : SettingsRegistryImplInternal
{
AZ : : SettingsRegistryInterface : : Type RapidjsonToSettingsRegistryType ( const rapidjson : : Value & value )
{
using Type = AZ : : SettingsRegistryInterface : : Type ;
switch ( value . GetType ( ) )
{
case rapidjson : : Type : : kNullType :
return Type : : Null ;
case rapidjson : : Type : : kFalseType :
return Type : : Boolean ;
case rapidjson : : Type : : kTrueType :
return Type : : Boolean ;
case rapidjson : : Type : : kObjectType :
return Type : : Object ;
case rapidjson : : Type : : kArrayType :
return Type : : Array ;
case rapidjson : : Type : : kStringType :
return Type : : String ;
case rapidjson : : Type : : kNumberType :
return value . IsDouble ( ) ? Type : : FloatingPoint :
Type : : Integer ;
}
return Type : : NoType ;
}
}
namespace AZ
{
template < typename T >
@ -28,7 +56,7 @@ namespace AZ
{
if ( path . empty ( ) )
{
// rapidjson::Pointer asse ts that the supplied string
// rapidjson::Pointer asse r ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
@ -70,7 +98,7 @@ namespace AZ
{
if ( path . empty ( ) )
{
// rapidjson::Pointer asse ts that the supplied string
// rapidjson::Pointer asse r ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
@ -161,7 +189,7 @@ namespace AZ
{
if ( path . empty ( ) )
{
// rapidjson::Pointer asse ts that the supplied string
// rapidjson::Pointer asse r ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
@ -212,9 +240,9 @@ namespace AZ
return Visit ( visitor , path ) ;
}
auto SettingsRegistryImpl : : RegisterNotifier ( const NotifyCallback & callback ) - > NotifyEventHandler
auto SettingsRegistryImpl : : RegisterNotifier ( NotifyCallback callback ) - > NotifyEventHandler
{
NotifyEventHandler notifyHandler { callback } ;
NotifyEventHandler notifyHandler { AZStd: : move ( callback) } ;
{
AZStd : : scoped_lock lock ( m_notifierMutex ) ;
notifyHandler . Connect ( m_notifiers ) ;
@ -222,14 +250,10 @@ namespace AZ
return notifyHandler ;
}
auto SettingsRegistryImpl : : RegisterNotifier ( Notify Callback& & callback ) - > NotifyEventHandler
auto SettingsRegistryImpl : : RegisterNotifier ( Notify EventHandler& notifyHandler ) - > void
{
NotifyEventHandler notifyHandler { AZStd : : move ( callback ) } ;
{
AZStd : : scoped_lock lock ( m_notifierMutex ) ;
notifyHandler . Connect ( m_notifiers ) ;
}
return notifyHandler ;
AZStd : : scoped_lock lock ( m_notifierMutex ) ;
notifyHandler . Connect ( m_notifiers ) ;
}
void SettingsRegistryImpl : : ClearNotifiers ( )
@ -238,9 +262,9 @@ namespace AZ
m_notifiers . DisconnectAllHandlers ( ) ;
}
auto SettingsRegistryImpl : : RegisterPreMergeEvent ( const PreMergeEventCallback & callback ) - > PreMergeEventHandler
auto SettingsRegistryImpl : : RegisterPreMergeEvent ( PreMergeEventCallback callback ) - > PreMergeEventHandler
{
PreMergeEventHandler preMergeHandler { callback } ;
PreMergeEventHandler preMergeHandler { AZStd: : move ( callback) } ;
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
preMergeHandler . Connect ( m_preMergeEvent ) ;
@ -248,19 +272,15 @@ namespace AZ
return preMergeHandler ;
}
auto SettingsRegistryImpl : : RegisterPreMergeEvent ( PreMergeEvent Callback& & callback ) - > PreMergeEventHandler
auto SettingsRegistryImpl : : RegisterPreMergeEvent ( PreMergeEvent Handler& preMergeHandler ) - > void
{
PreMergeEventHandler preMergeHandler { AZStd : : move ( callback ) } ;
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
preMergeHandler . Connect ( m_preMergeEvent ) ;
}
return preMergeHandler ;
AZStd : : scoped_lock lock ( m_settingMutex ) ;
preMergeHandler . Connect ( m_preMergeEvent ) ;
}
auto SettingsRegistryImpl : : RegisterPostMergeEvent ( const PostMergeEventCallback & callback ) - > PostMergeEventHandler
auto SettingsRegistryImpl : : RegisterPostMergeEvent ( PostMergeEventCallback callback ) - > PostMergeEventHandler
{
PostMergeEventHandler postMergeHandler { callback } ;
PostMergeEventHandler postMergeHandler { AZStd: : move ( callback) } ;
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
postMergeHandler . Connect ( m_postMergeEvent ) ;
@ -268,14 +288,10 @@ namespace AZ
return postMergeHandler ;
}
auto SettingsRegistryImpl : : RegisterPostMergeEvent ( PostMergeEvent Callback& & callback ) - > PostMergeEventHandler
auto SettingsRegistryImpl : : RegisterPostMergeEvent ( PostMergeEvent Handler& postMergeHandler ) - > void
{
PostMergeEventHandler postMergeHandler { AZStd : : move ( callback ) } ;
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
postMergeHandler . Connect ( m_postMergeEvent ) ;
}
return postMergeHandler ;
AZStd : : scoped_lock lock ( m_settingMutex ) ;
postMergeHandler . Connect ( m_postMergeEvent ) ;
}
void SettingsRegistryImpl : : ClearMergeEvents ( )
@ -297,7 +313,36 @@ namespace AZ
localNotifierEvent = AZStd : : move ( m_notifiers ) ;
}
localNotifierEvent . Signal ( jsonPath , type ) ;
// Signal the NotifyEvent for each queued argument
decltype ( m_signalNotifierQueue ) localNotifierQueue ;
{
AZStd : : scoped_lock signalLock ( m_signalMutex ) ;
m_signalNotifierQueue . push_back ( { FixedValueString { jsonPath } , type } ) ;
// If the signal count was 0, then a dispatch is in progress
if ( m_signalCount + + = = 0 )
{
AZStd : : swap ( localNotifierQueue , m_signalNotifierQueue ) ;
}
}
while ( ! localNotifierQueue . empty ( ) )
{
for ( SignalNotifierArgs notifierArgs : localNotifierQueue )
{
localNotifierEvent . Signal ( notifierArgs . m_jsonPath , notifierArgs . m_type ) ;
}
// Clear the local notifier queue and check if more notifiers have been added
localNotifierQueue = { } ;
{
AZStd : : scoped_lock signalLock ( m_signalMutex ) ;
AZStd : : swap ( localNotifierQueue , m_signalNotifierQueue ) ;
}
}
{
AZStd : : scoped_lock signalLock ( m_signalMutex ) ;
- - m_signalCount ;
}
{
// Swap the local handlers with the current m_notifiers which
@ -314,39 +359,19 @@ namespace AZ
{
if ( path . empty ( ) )
{
//rapidjson::Pointer asse ts that the supplied string
//rapidjson::Pointer asse r ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
}
rapidjson : : Pointer pointer ( path . data ( ) , path . length ( ) ) ;
if ( pointer . IsValid ( ) )
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
const rapidjson : : Value * value = pointer . Get ( m_settings ) ;
if ( value )
if ( const rapidjson : : Value * value = pointer . Get ( m_settings ) ; value ! = nullptr )
{
switch ( value - > GetType ( ) )
{
case rapidjson : : Type : : kNullType :
return Type : : Null ;
case rapidjson : : Type : : kFalseType :
return Type : : Boolean ;
case rapidjson : : Type : : kTrueType :
return Type : : Boolean ;
case rapidjson : : Type : : kObjectType :
return Type : : Object ;
case rapidjson : : Type : : kArrayType :
return Type : : Array ;
case rapidjson : : Type : : kStringType :
return Type : : String ;
case rapidjson : : Type : : kNumberType :
return
value - > IsDouble ( ) ? Type : : FloatingPoint :
Type : : Integer ;
}
return SettingsRegistryImplInternal : : RapidjsonToSettingsRegistryType ( * value ) ;
}
}
return Type : : NoType ;
@ -392,7 +417,7 @@ namespace AZ
{
if ( path . empty ( ) )
{
// rapidjson::Pointer asse ts that the supplied string
// rapidjson::Pointer asse r ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
@ -471,13 +496,12 @@ namespace AZ
{
if ( path . empty ( ) )
{
// rapidjson::Pointer assets that the supplied string
// rapidjson::Pointer asser ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
}
rapidjson : : Pointer pointer ( path . data ( ) , path . length ( ) ) ;
if ( pointer . IsValid ( ) )
{
@ -486,10 +510,14 @@ namespace AZ
value , nullptr , valueTypeID , m_serializationSettings ) ;
if ( jsonResult . GetProcessing ( ) ! = JsonSerializationResult : : Processing : : Halted )
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
rapidjson : : Value & setting = pointer . Create ( m_settings , m_settings . GetAllocator ( ) ) ;
setting = AZStd : : move ( store ) ;
SignalNotifier ( path , Type : : Object ) ;
auto anchorType = Type : : NoType ;
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
rapidjson : : Value & setting = pointer . Create ( m_settings , m_settings . GetAllocator ( ) ) ;
setting = AZStd : : move ( store ) ;
anchorType = SettingsRegistryImplInternal : : RapidjsonToSettingsRegistryType ( setting ) ;
}
SignalNotifier ( path , anchorType ) ;
return true ;
}
}
@ -500,7 +528,7 @@ namespace AZ
{
if ( path . empty ( ) )
{
// rapidjson::Pointer asse ts that the supplied string
// rapidjson::Pointer asse r ts that the supplied string
// is not nullptr even if the supplied size is 0
// Setting to empty string to prevent assert
path = " " ;
@ -605,7 +633,7 @@ namespace AZ
return Set ( key , value ) ;
}
bool SettingsRegistryImpl : : MergeSettings ( AZStd : : string_view data , Format format )
bool SettingsRegistryImpl : : MergeSettings ( AZStd : : string_view data , Format format , AZStd : : string_view anchorKey )
{
rapidjson : : Document jsonPatch ;
constexpr int flags = rapidjson : : kParseStopWhenDoneFlag | rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag ;
@ -631,17 +659,43 @@ namespace AZ
return false ;
}
AZStd : : scoped_lock lock ( m_settingMutex ) ;
rapidjson : : Pointer anchorPath ;
if ( ! anchorKey . empty ( ) )
{
anchorPath = rapidjson : : Pointer ( anchorKey . data ( ) , anchorKey . size ( ) ) ;
if ( ! anchorPath . IsValid ( ) )
{
rapidjson : : Pointer pointer ( AZ_SETTINGS_REGISTRY_HISTORY_KEY " /- " ) ;
AZ_Error ( " Settings Registry " , false , R " (Anchor path " % . * s " is invalid.) " , AZ_STRING_ARG ( anchorKey ) ) ;
AZStd : : scoped_lock lock ( m_settingMutex ) ;
pointer . Create ( m_settings , m_settings . GetAllocator ( ) ) . SetObject ( )
. AddMember ( rapidjson : : StringRef ( " Error " ) , rapidjson : : StringRef ( " Invalid anchor key. " ) , m_settings . GetAllocator ( ) )
. AddMember ( rapidjson : : StringRef ( " Path " ) ,
rapidjson : : Value ( anchorKey . data ( ) , aznumeric_caster ( anchorKey . size ( ) ) , m_settings . GetAllocator ( ) ) ,
m_settings . GetAllocator ( ) ) ;
return false ;
}
}
JsonSerializationResult : : ResultCode mergeResult =
JsonSerialization : : ApplyPatch ( m_settings , m_settings . GetAllocator ( ) , jsonPatch , mergeApproach ) ;
if ( mergeResult . GetProcessing ( ) ! = JsonSerializationResult : : Processing : : Completed )
auto anchorType = AZ : : SettingsRegistryInterface : : Type : : NoType ;
{
AZ_Error ( " Settings Registry " , false , " Failed to fully merge data into registry. " ) ;
return false ;
AZStd : : scoped_lock lock ( m_settingMutex ) ;
rapidjson : : Value & anchorRoot = anchorPath . IsValid ( ) ? anchorPath . Create ( m_settings , m_settings . GetAllocator ( ) )
: m_settings ;
JsonSerializationResult : : ResultCode mergeResult =
JsonSerialization : : ApplyPatch ( anchorRoot , m_settings . GetAllocator ( ) , jsonPatch , mergeApproach ) ;
if ( mergeResult . GetProcessing ( ) ! = JsonSerializationResult : : Processing : : Completed )
{
AZ_Error ( " Settings Registry " , false , " Failed to fully merge data into registry. " ) ;
return false ;
}
// The settings have been successfully merged, query the type at the anchor key
anchorType = SettingsRegistryImplInternal : : RapidjsonToSettingsRegistryType ( anchorRoot ) ;
}
SignalNotifier ( " " , Type : : Object ) ;
SignalNotifier ( anchorKey , anchorType ) ;
return true ;
}
@ -1225,10 +1279,12 @@ namespace AZ
ScopedMergeEvent scopedMergeEvent ( m_preMergeEvent , m_postMergeEvent , path , rootKey ) ;
JsonSerializationResult : : ResultCode mergeResult ( JsonSerializationResult : : Tasks : : Merge ) ;
auto anchorType = Type : : NoType ;
if ( rootKey . empty ( ) )
{
AZStd : : scoped_lock lock ( m_settingMutex ) ;
mergeResult = JsonSerialization : : ApplyPatch ( m_settings , m_settings . GetAllocator ( ) , jsonPatch , mergeApproach , m_applyPatchSettings ) ;
anchorType = SettingsRegistryImplInternal : : RapidjsonToSettingsRegistryType ( m_settings ) ;
}
else
{
@ -1238,6 +1294,7 @@ namespace AZ
AZStd : : scoped_lock lock ( m_settingMutex ) ;
Value & rootValue = root . Create ( m_settings , m_settings . GetAllocator ( ) ) ;
mergeResult = JsonSerialization : : ApplyPatch ( rootValue , m_settings . GetAllocator ( ) , jsonPatch , mergeApproach , m_applyPatchSettings ) ;
anchorType = SettingsRegistryImplInternal : : RapidjsonToSettingsRegistryType ( rootValue ) ;
}
else
{
@ -1265,7 +1322,7 @@ namespace AZ
pointer . Create ( m_settings , m_settings . GetAllocator ( ) ) . SetString ( path , m_settings . GetAllocator ( ) ) ;
}
SignalNotifier ( " " , Type : : Object ) ;
SignalNotifier ( rootKey , anchorType ) ;
return true ;
}