浏览代码

Settings Registry Visitor API Updates (#11435)

* Settings Registry Visitor API Updates

[[Breaking Change]] The Visit functions on the Settings Registry now supply a struct with the arguments about the field being visited.

This allows future updates to Visit API without changing the API in the future.
The new struct now supplies the `SettingsType` value for the field type, instead of `Type` allowing for querying the signedness of an integer type.
The struct also supplies a reference to the Settings Registry being visited, alliviating the need to bind the SettingsRegistry to the visitor or functor, supplied to visit.

Greatly simplified the PlatformConfiguration parsing of the settings registry for AssetProcessor settings, the LocalBookmarkLoader for the editor bookrmark settings and the StreamerConfiguration for determining if a drive is in use by O3DE

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed encoding of '/' and '~' characters in the reference token of a JSON pointer in the StackedString Push function.

The StackedString class did not encode the characters of '~' -> '~0' and '/' -> '~1' when pushing a new element onto a JSON pointer path.
So if a settings key contained a '/' slash such as in the[AssetProcessorPlatformConfig.setreg](https://github.com/o3de/o3de/blob/development/Registry/AssetProcessorPlatformConfig.setreg#L129) attempting to use the supplied JSON pointer path in a visitor callback to query a setting in the Setting Registry, it would fail

Signed-off-by: lumberyard-employee-dm <[email protected]>

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 2 年之前
父节点
当前提交
139e535cb8
共有 28 个文件被更改,包括 774 次插入1148 次删除
  1. 15 11
      Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
  2. 32 5
      Code/Framework/AzCore/AzCore/Serialization/Json/StackedString.cpp
  3. 10 0
      Code/Framework/AzCore/AzCore/Serialization/Json/StackedString.h
  4. 35 15
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h
  5. 91 37
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
  6. 5 3
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.h
  7. 43 39
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
  8. 6 0
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
  9. 23 12
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.cpp
  10. 9 7
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.h
  11. 16 52
      Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StreamerConfiguration_Windows.cpp
  12. 3 3
      Code/Framework/AzCore/Tests/Settings/SettingsRegistryMergeUtilsTests.cpp
  13. 22 22
      Code/Framework/AzCore/Tests/Settings/SettingsRegistryTests.cpp
  14. 21 18
      Code/Framework/AzCore/Tests/Settings/SettingsRegistryVisitorUtilsTests.cpp
  15. 20 50
      Code/Framework/AzFramework/AzFramework/Application/Application.cpp
  16. 6 9
      Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/SettingsRegistryAdapter.cpp
  17. 1 1
      Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/SettingsRegistryAdapter.h
  18. 11 9
      Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp
  19. 16 18
      Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp
  20. 65 70
      Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/LocalViewBookmarkLoader.cpp
  21. 3 3
      Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/LocalViewBookmarkLoader.h
  22. 4 4
      Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewBookmarkLoaderInterface.h
  23. 3 3
      Code/Framework/AzToolsFramework/Tests/Viewport/ViewBookmarkTests.cpp
  24. 25 145
      Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp
  25. 0 28
      Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.h
  26. 63 26
      Code/Tools/AssetProcessor/native/tests/InternalBuilders/SettingsRegistryBuilderTests.cpp
  27. 225 451
      Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp
  28. 1 107
      Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h

+ 15 - 11
Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp

@@ -1256,17 +1256,15 @@ namespace AZ
             bool m_autoLoad{ true };
         };
 
-
         using FixedValueString = SettingsRegistryInterface::FixedValueString;
-        using Type = SettingsRegistryInterface::Type;
         AZStd::vector<GemModuleLoadData> modulesLoadData;
         auto GemModuleVisitor = [&settingsRegistry = *m_settingsRegistry, &modulesLoadData]
-        (AZStd::string_view gemRootObjectPath, AZStd::string_view, Type)
+        (const AZ::SettingsRegistryInterface::VisitArgs& activeGemArgs)
         {
-            auto VisitGemObjectFields = [&settingsRegistry, &modulesLoadData](AZStd::string_view jsonPath,
-                AZStd::string_view fieldName, Type)
+            auto VisitGemObjectFields = [&settingsRegistry, &modulesLoadData](
+                const AZ::SettingsRegistryInterface::VisitArgs& gemTargetArgs)
             {
-                AZStd::string_view gemModuleName = fieldName;
+                AZStd::string_view gemModuleName = gemTargetArgs.m_fieldName;
                 auto FindGemModuleLoadEntry = [gemModuleName](const GemModuleLoadData& moduleLoadData)
                 {
                     return gemModuleName == moduleLoadData.m_gemModuleName;
@@ -1280,7 +1278,7 @@ namespace AZ
                 // By default the auto load option is true
                 // So auto load is turned off if option "AutoLoad" key exist and has a value of false
                 auto autoLoadJsonPath = FixedValueString::format("%.*s/AutoLoad",
-                    AZ_STRING_ARG(jsonPath));
+                    AZ_STRING_ARG(gemTargetArgs.m_jsonKeyPath));
                 if (bool autoLoadModule{}; settingsRegistry.Get(autoLoadModule, autoLoadJsonPath) && !autoLoadModule)
                 {
                     moduleLoadData.m_autoLoad = false;
@@ -1288,20 +1286,26 @@ namespace AZ
 
                 // Locate the Module paths within the Gem Target Name object
                 auto AppendDynamicModulePaths = [&settingsRegistry, &moduleLoadData]
-                (AZStd::string_view gemModuleJsonPath, AZStd::string_view, Type)
+                (const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
                 {
-                    if (AZ::IO::Path modulePath; settingsRegistry.Get(modulePath.Native(), gemModuleJsonPath))
+                    if (AZ::IO::Path modulePath; settingsRegistry.Get(modulePath.Native(), visitArgs.m_jsonKeyPath))
                     {
                         moduleLoadData.m_dynamicLibraryPaths.emplace_back(AZStd::move(modulePath.Native()));
                     }
+
+                    return AZ::SettingsRegistryInterface::VisitResponse::Skip;
                 };
                 auto gemModulesJsonPath = FixedValueString::format("%.*s/Modules",
-                    AZ_STRING_ARG(jsonPath));
+                    AZ_STRING_ARG(gemTargetArgs.m_jsonKeyPath));
                 AZ::SettingsRegistryVisitorUtils::VisitArray(settingsRegistry, AppendDynamicModulePaths, gemModulesJsonPath);
+
+                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
             };
 
             AZ::SettingsRegistryVisitorUtils::VisitField(settingsRegistry, VisitGemObjectFields,
-                FixedValueString::format("%.*s/Targets", AZ_STRING_ARG(gemRootObjectPath)));
+                FixedValueString::format("%.*s/Targets", AZ_STRING_ARG(activeGemArgs.m_jsonKeyPath)));
+
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         ModuleDescriptorList gemModules;

+ 32 - 5
Code/Framework/AzCore/AzCore/Serialization/Json/StackedString.cpp

@@ -7,9 +7,30 @@
  */
 
 #include <AzCore/Serialization/Json/StackedString.h>
+#include <AzCore/std/string/fixed_string.h>
+#include <AzCore/std/ranges/join_view.h>
+#include <AzCore/std/ranges/transform_view.h>
 
 namespace AZ
 {
+    //! For JSON Pointer "~" is the escape character and "/"
+    //! indicate the start of a reference token
+    //! They escape to '~' -> "~0' and `/` = "~1"
+    //! @return a fixed_string with max size for two characters
+    //! to accomodate the escape tokens
+    [[nodiscard]] static AZStd::fixed_string<2> EncodeJsonPointerCharacter(char elem)
+    {
+        switch (elem)
+        {
+        case JsonPointerEscape:
+            return AZStd::fixed_string<2>(JsonPointerEncodedEscape);
+        case JsonPointerReferenceTokenPrefix:
+            return AZStd::fixed_string<2>(JsonPointerEncodedReferenceTokenPrefix);
+        default:
+            return AZStd::fixed_string<2>{ elem };
+        }
+    }
+
     StackedString::StackedString(Format format)
         : m_format(format)
     {
@@ -28,19 +49,25 @@ namespace AZ
                 {
                     m_string += '.';
                 }
-                break;
+                m_string += value;
+                return;
             case Format::JsonPointer:
                 m_string += '/';
-                break;
+                // encode the escape characters in the reference token and flatten the view of fixed_strings
+                // to allow iteration over each chacter
+                for (char elem : value | AZStd::views::transform(&EncodeJsonPointerCharacter) | AZStd::views::join)
+                {
+                    m_string += elem;
+                }
+                return;
             default:
                 if (!m_string.empty())
                 {
                     m_string += ' ';
                 }
-                break;
+                m_string += value;
+                return;
             }
-
-            m_string.append(value.data(), value.length());
         }
     }
     

+ 10 - 0
Code/Framework/AzCore/AzCore/Serialization/Json/StackedString.h

@@ -14,6 +14,11 @@
 
 namespace AZ
 {
+    constexpr char JsonPointerEscape = '~';
+    constexpr AZStd::string_view JsonPointerEncodedEscape = "~0";
+    constexpr char JsonPointerReferenceTokenPrefix = '/';
+    constexpr AZStd::string_view JsonPointerEncodedReferenceTokenPrefix = "~1";
+
     class StackedString
     {
     public:
@@ -26,6 +31,11 @@ namespace AZ
         explicit StackedString(Format format);
 
         //! Push a new string part onto the stack.
+        //! Only supports a single reference token
+        //! i.e appending "Foo" onto "/O3DE" results in "/O3DE/Foo"
+        //! However appending "TokenWith/ForwardSlash" results in "/O3DE/Foo/TokenWith~1ForwardSlash"
+        //! as the forward slash is seen as part of a single reference token and encoded.
+        //! See the JSON pointer path spec for more info: https://www.rfc-editor.org/rfc/rfc6901#section-3
         void Push(AZStd::string_view value);
         //! Push an integer value to the stack. The value will be converted to a string.
         void Push(size_t value);

+ 35 - 15
Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h

@@ -142,7 +142,7 @@ namespace AZ
         struct NotifyEventArgs
         {
             AZStd::string_view m_jsonKeyPath;
-            Type m_type = Type::NoType;
+            SettingsType m_type;
             AZStd::string_view m_mergeFilePath;
         };
         using NotifyCallback = AZStd::function<void(const NotifyEventArgs& notifierArgs)>;
@@ -161,8 +161,28 @@ namespace AZ
         using PreMergeEventHandler = typename PreMergeEvent::Handler;
         using PostMergeEventHandler = typename PostMergeEvent::Handler;
 
+        //! Stores the data about the settings field being visited
+        //! The full key path to the settings field is supplied, along with the settings type
+        struct VisitArgs
+        {
+            VisitArgs(const AZ::SettingsRegistryInterface& registry)
+                : m_registry(registry)
+            {}
+
+            //! Full key path to the settings field being visited. Includes field name
+            //! i.e "/O3DE/Settings/FieldName"
+            AZStd::string_view m_jsonKeyPath;
+            //! The specific field name being visited. The field name is parented to an object or array
+            //! i.e "FieldName"
+            AZStd::string_view m_fieldName;
+            //! The type of the setting stored within the registry.
+            SettingsType m_type;
+            //! Reference to the Settings Registry instance performing the visit operations
+            const AZ::SettingsRegistryInterface& m_registry;
+        };
+
         using VisitorCallback =
-            AZStd::function<VisitResponse(AZStd::string_view path, AZStd::string_view valueName, VisitAction action, Type type)>;
+            AZStd::function<VisitResponse(const VisitArgs&, VisitAction action)>;
         //! Base class for the visitor class during traversal over the Settings Registry. The type-agnostic function is always
         //! called and, if applicable, the overloaded functions with the appropriate values.
         class Visitor
@@ -170,18 +190,18 @@ namespace AZ
         public:
             virtual ~Visitor() = 0;
 
-            virtual VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName, VisitAction action, Type type)
-            { AZ_UNUSED(path); AZ_UNUSED(valueName); AZ_UNUSED(action); AZ_UNUSED(type); return VisitResponse::Continue; }
-            virtual void Visit(AZStd::string_view path, AZStd::string_view valueName, Type type, bool value)
-            { AZ_UNUSED(path); AZ_UNUSED(valueName); AZ_UNUSED(type); AZ_UNUSED(value); }
-            virtual void Visit(AZStd::string_view path, AZStd::string_view valueName, Type type, s64 value)
-            { AZ_UNUSED(path); AZ_UNUSED(valueName); AZ_UNUSED(type); AZ_UNUSED(value); }
-            virtual void Visit(AZStd::string_view path, AZStd::string_view valueName, Type type, u64 value)
-            { AZ_UNUSED(path); AZ_UNUSED(valueName); AZ_UNUSED(type); AZ_UNUSED(value); }
-            virtual void Visit(AZStd::string_view path, AZStd::string_view valueName, Type type, double value)
-            { AZ_UNUSED(path); AZ_UNUSED(valueName); AZ_UNUSED(type); AZ_UNUSED(value); }
-            virtual void Visit(AZStd::string_view path, AZStd::string_view valueName, Type type, AZStd::string_view value)
-            { AZ_UNUSED(path); AZ_UNUSED(valueName); AZ_UNUSED(type); AZ_UNUSED(value); }
+            virtual VisitResponse Traverse([[maybe_unused]] const VisitArgs& visitArgs, [[maybe_unused]] VisitAction action)
+            { return VisitResponse::Continue; }
+            virtual void Visit([[maybe_unused]] const VisitArgs& visitArgs, [[maybe_unused]] bool value)
+            {}
+            virtual void Visit([[maybe_unused]] const VisitArgs& visitArgs, [[maybe_unused]] s64 value)
+            {}
+            virtual void Visit([[maybe_unused]] const VisitArgs& visitArgs, [[maybe_unused]] u64 value)
+            {}
+            virtual void Visit([[maybe_unused]] const VisitArgs& visitArgs, [[maybe_unused]] double value)
+            {}
+            virtual void Visit([[maybe_unused]] const VisitArgs& visitArgs, [[maybe_unused]] AZStd::string_view value)
+            {}
         };
 
         SettingsRegistryInterface() = default;
@@ -189,7 +209,7 @@ namespace AZ
         virtual ~SettingsRegistryInterface() = default;
 
         //! Returns the type of an entry in the Settings Registry or Type::None if there's no value or the path is invalid.
-        virtual SettingsType GetType(AZStd::string_view path) const = 0;
+        [[nodiscard]] virtual SettingsType GetType(AZStd::string_view path) const = 0;
         //! Traverses over the entries in the Settings Registry. Use this version to retrieve the values of entries as well.
         //! @param visitor An instance of a class derived from Visitor that will repeatedly be called as entries are encountered.
         //! @param path An offset at which traversal should start.

+ 91 - 37
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp

@@ -16,14 +16,14 @@
 #include <AzCore/Serialization/Json/JsonSerialization.h>
 #include <AzCore/Serialization/Json/StackedString.h>
 #include <AzCore/Settings/SettingsRegistryImpl.h>
-#include <AzCore/std/containers/variant.h>
 #include <AzCore/std/sort.h>
 #include <AzCore/std/parallel/scoped_lock.h>
 #include <AzCore/std/ranges/ranges_algorithm.h>
+#include <AzCore/std/ranges/split_view.h>
 
 namespace AZ::SettingsRegistryImplInternal
 {
-    AZ::SettingsRegistryInterface::Type RapidjsonToSettingsRegistryType(const rapidjson::Value& value)
+    [[nodiscard]] AZ::SettingsRegistryInterface::Type RapidjsonToSettingsRegistryType(const rapidjson::Value& value)
     {
         using Type = AZ::SettingsRegistryInterface::Type;
         switch (value.GetType())
@@ -232,7 +232,9 @@ namespace AZ
                 if (!path.empty())
                 {
                     path.remove_prefix(1); // Remove the leading slash as the StackedString will add this back in.
-                    jsonPath.Push(path);
+                    // Push each JSON pointer reference token to avoid '/' being encoded
+                    AZStd::ranges::for_each(path | AZStd::views::split(JsonPointerReferenceTokenPrefix),
+                        [&jsonPath](AZStd::string_view refToken) { jsonPath.Push(refToken); });
                 }
                 // Extract the last token of the JSON pointer to use as the valueName
                 AZStd::string_view valueName;
@@ -255,9 +257,9 @@ namespace AZ
         {
             explicit CallbackVisitor(const VisitorCallback& callback) : m_callback(callback) {};
 
-            VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName, VisitAction action, Type type) override
+            VisitResponse Traverse(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, VisitAction action) override
             {
-                return m_callback(path, valueName, action, type);
+                return m_callback(visitArgs, action);
             }
 
             const VisitorCallback& m_callback;
@@ -327,7 +329,7 @@ namespace AZ
         m_postMergeEvent.DisconnectAllHandlers();
     }
 
-    void SettingsRegistryImpl::SignalNotifier(AZStd::string_view jsonPath, Type type)
+    void SettingsRegistryImpl::SignalNotifier(AZStd::string_view jsonPath, SettingsType type)
     {
         // Move the Notifier AZ::Event to a local AZ::Event in order to allow
         // the notifier handlers to be signaled outside of the notifier mutex
@@ -384,11 +386,11 @@ namespace AZ
         }
     }
 
-    SettingsRegistryInterface::SettingsType SettingsRegistryImpl::GetType(AZStd::string_view path) const
+    [[nodiscard]] SettingsRegistryInterface::SettingsType SettingsRegistryImpl::GetType(AZStd::string_view path) const
     {
         if (path.empty())
         {
-            //rapidjson::Pointer asserts that the supplied string
+            // rapidjson::Pointer asserts that the supplied string
             // is not nullptr even if the supplied size is 0
             // Setting to empty string to prevent assert
             path = "";
@@ -398,6 +400,23 @@ namespace AZ
         if (pointer.IsValid())
         {
             AZStd::scoped_lock lock(m_settingMutex);
+            return GetTypeNoLock(path);
+        }
+        return SettingsType{};
+    }
+
+    [[nodiscard]] SettingsRegistryInterface::SettingsType SettingsRegistryImpl::GetTypeNoLock(AZStd::string_view path) const
+    {
+        if (path.empty())
+        {
+            // rapidjson::Pointer asserts 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())
+        {
             if (const rapidjson::Value* value = pointer.Get(m_settings); value != nullptr)
             {
                 SettingsType type;
@@ -483,7 +502,7 @@ namespace AZ
         {
             return false;
         }
-        SignalNotifier(path, Type::Boolean);
+        SignalNotifier(path, { Type::Boolean });
         return true;
     }
 
@@ -493,7 +512,7 @@ namespace AZ
         {
             return false;
         }
-        SignalNotifier(path, Type::Integer);
+        SignalNotifier(path, { Type::Integer, Signedness::Signed });
         return true;
     }
 
@@ -503,7 +522,7 @@ namespace AZ
         {
             return false;
         }
-        SignalNotifier(path, Type::Integer);
+        SignalNotifier(path, { Type::Integer, Signedness::Unsigned });
         return true;
     }
 
@@ -513,7 +532,7 @@ namespace AZ
         {
             return false;
         }
-        SignalNotifier(path, Type::FloatingPoint);
+        SignalNotifier(path, { Type::FloatingPoint });
         return true;
     }
 
@@ -523,7 +542,7 @@ namespace AZ
         {
             return false;
         }
-        SignalNotifier(path, Type::String);
+        SignalNotifier(path, SettingsType{ Type::String });
         return true;
     }
 
@@ -550,12 +569,12 @@ namespace AZ
                 value, nullptr, valueTypeID, m_serializationSettings);
             if (jsonResult.GetProcessing() != JsonSerializationResult::Processing::Halted)
             {
-                auto anchorType = Type::NoType;
+                SettingsType anchorType;
                 {
                     AZStd::scoped_lock lock(m_settingMutex);
                     rapidjson::Value& setting = pointer.Create(m_settings, m_settings.GetAllocator());
                     setting = AZStd::move(store);
-                    anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(setting);
+                    anchorType = GetTypeNoLock(path);
                 }
                 SignalNotifier(path, anchorType);
                 return true;
@@ -751,7 +770,7 @@ namespace AZ
             };
         }
 
-        auto anchorType = AZ::SettingsRegistryInterface::Type::NoType;
+        SettingsType anchorType;
         {
             AZStd::scoped_lock lock(m_settingMutex);
             rapidjson::Value& anchorRoot = anchorPath.IsValid() ? anchorPath.Create(m_settings, m_settings.GetAllocator())
@@ -766,7 +785,7 @@ namespace AZ
             }
 
             // The settings have been successfully merged, query the type at the anchor key
-            anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(anchorRoot);
+            anchorType = GetTypeNoLock(anchorKey);
         }
 
         SignalNotifier(anchorKey, anchorType);
@@ -991,27 +1010,40 @@ namespace AZ
         const rapidjson::Value& value) const
     {
         VisitResponse result;
+        VisitArgs visitArgs(*this);
         switch (value.GetType())
         {
         case rapidjson::Type::kNullType:
-            result = visitor.Traverse(path, valueName, VisitAction::Value, Type::Null);
+            visitArgs.m_jsonKeyPath = path;
+            visitArgs.m_fieldName = valueName;
+            visitArgs.m_type = { Type::Null };
+            result = visitor.Traverse(visitArgs, VisitAction::Value);
             break;
         case rapidjson::Type::kFalseType:
-            result = visitor.Traverse(path, valueName, VisitAction::Value, Type::Boolean);
+            visitArgs.m_jsonKeyPath = path;
+            visitArgs.m_fieldName = valueName;
+            visitArgs.m_type = { Type::Boolean };
+            result = visitor.Traverse(visitArgs, VisitAction::Value);
             if (result == VisitResponse::Continue)
             {
-                visitor.Visit(path, valueName, Type::Boolean, false);
+                visitor.Visit(visitArgs, false);
             }
             break;
         case rapidjson::Type::kTrueType:
-            result = visitor.Traverse(path, valueName, VisitAction::Value, Type::Boolean);
+            visitArgs.m_jsonKeyPath = path;
+            visitArgs.m_fieldName = valueName;
+            visitArgs.m_type = { Type::Boolean };
+            result = visitor.Traverse(visitArgs, VisitAction::Value);
             if (result == VisitResponse::Continue)
             {
-                visitor.Visit(path, valueName, Type::Boolean, true);
+                visitor.Visit(visitArgs, true);
             }
             break;
         case rapidjson::Type::kObjectType:
-            result = visitor.Traverse(path, valueName, VisitAction::Begin, Type::Object);
+            visitArgs.m_jsonKeyPath = path;
+            visitArgs.m_fieldName = valueName;
+            visitArgs.m_type = { Type::Object };
+            result = visitor.Traverse(visitArgs, VisitAction::Begin);
             if (result == VisitResponse::Continue)
             {
                 for (const auto& member : value.GetObject())
@@ -1024,14 +1056,22 @@ namespace AZ
                     }
                     path.Pop();
                 }
-                if (visitor.Traverse(path, valueName, VisitAction::End, Type::Object) == VisitResponse::Done)
+
+                // Must refresh the m_jsonKeyPath string view as the for loop would modify the StackedString
+                // and therefore invalidate the string_view
+                visitArgs.m_jsonKeyPath = path;
+                visitArgs.m_fieldName = valueName;
+                if (visitor.Traverse(visitArgs, VisitAction::End) == VisitResponse::Done)
                 {
                     return VisitResponse::Done;
                 }
             }
             break;
         case rapidjson::Type::kArrayType:
-            result = visitor.Traverse(path, valueName, VisitAction::Begin, Type::Array);
+            visitArgs.m_jsonKeyPath = path;
+            visitArgs.m_fieldName = valueName;
+            visitArgs.m_type = { Type::Array };
+            result = visitor.Traverse(visitArgs, VisitAction::Begin);
             if (result == VisitResponse::Continue)
             {
                 size_t counter = 0;
@@ -1050,42 +1090,56 @@ namespace AZ
                     counter++;
                     path.Pop();
                 }
-                if (visitor.Traverse(path, valueName, VisitAction::End, Type::Array) == VisitResponse::Done)
+
+                // Must refresh the m_jsonKeyPath string view as the for loop would modify the StackedString
+                // and therefore invalidate the string_view
+                visitArgs.m_jsonKeyPath = path;
+                visitArgs.m_fieldName = valueName;
+                if (visitor.Traverse(visitArgs, VisitAction::End) == VisitResponse::Done)
                 {
                     return VisitResponse::Done;
                 }
             }
             break;
         case rapidjson::Type::kStringType:
-            result = visitor.Traverse(path, valueName, VisitAction::Value, Type::String);
+            visitArgs.m_jsonKeyPath = path;
+            visitArgs.m_fieldName = valueName;
+            visitArgs.m_type = { Type::String };
+            result = visitor.Traverse(visitArgs, VisitAction::Value);
             if (result == VisitResponse::Continue)
             {
-                visitor.Visit(path, valueName, Type::String, AZStd::string_view(value.GetString(), value.GetStringLength()));
+                visitor.Visit(visitArgs, AZStd::string_view(value.GetString(), value.GetStringLength()));
             }
             break;
         case rapidjson::Type::kNumberType:
             if (value.IsDouble())
             {
-                result = visitor.Traverse(path, valueName, VisitAction::Value, Type::FloatingPoint);
+                visitArgs.m_jsonKeyPath = path;
+                visitArgs.m_fieldName = valueName;
+                visitArgs.m_type = { Type::FloatingPoint };
+                result = visitor.Traverse(visitArgs, VisitAction::Value);
                 if (result == VisitResponse::Continue)
                 {
-                    visitor.Visit(path, valueName, Type::FloatingPoint, value.GetDouble());
+                    visitor.Visit(visitArgs, value.GetDouble());
                 }
             }
             else
             {
-                result = visitor.Traverse(path, valueName, VisitAction::Value, Type::Integer);
+                visitArgs.m_jsonKeyPath = path;
+                visitArgs.m_fieldName = valueName;
+                visitArgs.m_type = { Type::Integer, value.IsInt64() ? Signedness::Signed : Signedness::Unsigned };
+                result = visitor.Traverse(visitArgs, VisitAction::Value);
                 if (result == VisitResponse::Continue)
                 {
-                    if (value.IsInt64())
+                    if (visitArgs.m_type.m_signedness == Signedness::Signed)
                     {
                         s64 integerValue = value.GetInt64();
-                        visitor.Visit(path, valueName, Type::Integer, integerValue);
+                        visitor.Visit(visitArgs, integerValue);
                     }
                     else
                     {
                         u64 integerValue = value.GetUint64();
-                        visitor.Visit(path, valueName, Type::Integer, integerValue);
+                        visitor.Visit(visitArgs, integerValue);
                     }
                 }
             }
@@ -1393,12 +1447,12 @@ namespace AZ
 
         ScopedMergeEvent scopedMergeEvent(*this, { path, rootKey });
         JsonSerializationResult::ResultCode mergeResult(JsonSerializationResult::Tasks::Merge);
-        auto anchorType = Type::NoType;
+        SettingsType anchorType;
         if (rootKey.empty())
         {
             AZStd::scoped_lock lock(m_settingMutex);
             mergeResult = JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach, applyPatchSettings);
-            anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(m_settings);
+            anchorType = GetTypeNoLock(rootKey);
         }
         else
         {
@@ -1408,7 +1462,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, applyPatchSettings);
-                anchorType = SettingsRegistryImplInternal::RapidjsonToSettingsRegistryType(rootValue);
+                anchorType = GetTypeNoLock(rootKey);
             }
             else
             {

+ 5 - 3
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.h

@@ -45,7 +45,7 @@ namespace AZ
         void SetContext(SerializeContext* context);
         void SetContext(JsonRegistrationContext* context);
         
-        SettingsType GetType(AZStd::string_view path) const override;
+        [[nodiscard]] SettingsType GetType(AZStd::string_view path) const override;
         bool Visit(Visitor& visitor, AZStd::string_view path) const override;
         bool Visit(const VisitorCallback& callback, AZStd::string_view path) const override;
         [[nodiscard]] NotifyEventHandler RegisterNotifier(NotifyCallback callback) override;
@@ -100,6 +100,8 @@ namespace AZ
         };
         using RegistryFileList = AZStd::fixed_vector<RegistryFile, MaxRegistryFolderEntries>;
 
+        [[nodiscard]] SettingsType GetTypeNoLock(AZStd::string_view path) const;
+
         template<typename T>
         bool SetValueInternal(AZStd::string_view path, T value);
         template<typename T>
@@ -113,7 +115,7 @@ namespace AZ
         bool ExtractFileDescription(RegistryFile& output, AZStd::string_view filename, const Specializations& specializations);
         bool MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey, AZStd::vector<char>& scratchBuffer);
 
-        void SignalNotifier(AZStd::string_view jsonPath, Type type);
+        void SignalNotifier(AZStd::string_view jsonPath, SettingsType type);
 
         
         mutable AZStd::recursive_mutex m_settingMutex;
@@ -130,7 +132,7 @@ namespace AZ
         struct SignalNotifierArgs
         {
             FixedValueString m_jsonPath;
-            Type m_type;
+            SettingsType m_type;
             AZ::IO::FixedMaxPath m_mergeFilePath;
         };
         AZStd::deque<SignalNotifierArgs> m_signalNotifierQueue;

+ 43 - 39
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp

@@ -89,10 +89,9 @@ namespace AZ::Internal
             {
                 using AZ::SettingsRegistryInterface::Visitor::Visit;
                 void Visit(
-                    [[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
-                    [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
+                    const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZStd::string_view value) override
                 {
-                    m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} });
+                    m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{visitArgs.m_fieldName} });
                     // Make sure any engine paths read from the manifest are absolute
                     AZ::IO::FixedMaxPath& recentEnginePath = m_enginePaths.back().m_path;
                     if (recentEnginePath.IsRelative())
@@ -621,14 +620,13 @@ namespace AZ::SettingsRegistryMergeUtils
             {}
 
             using AZ::SettingsRegistryInterface::Visitor::Visit;
-            void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
-                [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, bool value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, bool value) override
             {
                 if (value)
                 {
                     // The specialization is the key itself.
                     // The value is just used to determine if the specialization should be added
-                    m_settingsSpecialization.Append(valueName);
+                    m_settingsSpecialization.Append(visitArgs.m_fieldName);
                 }
             }
             SettingsRegistryInterface::Specializations& m_settingsSpecialization;
@@ -1152,14 +1150,14 @@ namespace AZ::SettingsRegistryMergeUtils
             : AZ::SettingsRegistryInterface::Visitor
         {
             using AZ::SettingsRegistryInterface::Visitor::Visit;
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type
-                , AZStd::string_view value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
+                AZStd::string_view value) override
             {
-                if (valueName == "Option" && !value.empty())
+                if (visitArgs.m_fieldName == "Option" && !value.empty())
                 {
-                    m_arguments.push_back(AZStd::string::format("--%.*s", aznumeric_cast<int>(value.size()), value.data()));
+                    m_arguments.push_back(AZStd::string::format("--%.*s", AZ_STRING_ARG(value)));
                 }
-                else if (valueName == "Value" && !value.empty())
+                else if (visitArgs.m_fieldName == "Value" && !value.empty())
                 {
                     // Make sure value types are in quotes in case they start with a command option prefix
                     m_arguments.push_back(QuoteArgument(value));
@@ -1168,7 +1166,7 @@ namespace AZ::SettingsRegistryMergeUtils
 
             AZStd::string QuoteArgument(AZStd::string_view arg)
             {
-                return !arg.empty() ? AZStd::string::format(R"("%.*s")", aznumeric_cast<int>(arg.size()), arg.data()) : AZStd::string{ arg };
+                return !arg.empty() ? AZStd::string::format(R"("%.*s")", AZ_STRING_ARG(arg)) : AZStd::string{arg};
             }
 
             // The first parameter is skipped by the ComamndLine::Parse function so initialize
@@ -1261,19 +1259,18 @@ namespace AZ::SettingsRegistryMergeUtils
                 return true;
             }
             AZ::SettingsRegistryInterface::VisitResponse Traverse(
-                AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action,
-                AZ::SettingsRegistryInterface::Type type) override
+                const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZ::SettingsRegistryInterface::VisitAction action) override
             {
                 // Pass the pointer path to the inclusion filter if available
-                if (m_dumperSettings.m_includeFilter && !m_dumperSettings.m_includeFilter(path))
+                if (m_dumperSettings.m_includeFilter && !m_dumperSettings.m_includeFilter(visitArgs.m_jsonKeyPath))
                 {
                     return AZ::SettingsRegistryInterface::VisitResponse::Skip;
                 }
 
                 if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
                 {
-                    m_result = m_result && WriteName(valueName);
-                    if (type == AZ::SettingsRegistryInterface::Type::Object)
+                    m_result = m_result && WriteName(visitArgs.m_fieldName);
+                    if (visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Object)
                     {
                         m_result = m_result && m_writer.StartObject();
                         if (m_result)
@@ -1292,7 +1289,7 @@ namespace AZ::SettingsRegistryMergeUtils
                 }
                 else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
                 {
-                    if (type == AZ::SettingsRegistryInterface::Type::Object)
+                    if (visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Object)
                     {
                         m_result = m_result && m_writer.EndObject();
                     }
@@ -1305,9 +1302,9 @@ namespace AZ::SettingsRegistryMergeUtils
                 }
                 else
                 {
-                    if (type == AZ::SettingsRegistryInterface::Type::Null)
+                    if (visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Null)
                     {
-                        m_result = m_result && WriteName(valueName) && m_writer.Null();
+                        m_result = m_result && WriteName(visitArgs.m_fieldName) && m_writer.Null();
                     }
                 }
 
@@ -1316,29 +1313,29 @@ namespace AZ::SettingsRegistryMergeUtils
                     AZ::SettingsRegistryInterface::VisitResponse::Done;
             }
 
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, bool value) override
             {
-                m_result = m_result && WriteName(valueName) && m_writer.Bool(value);
+                m_result = m_result && WriteName(visitArgs.m_fieldName) && m_writer.Bool(value);
             }
 
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZ::s64 value) override
             {
-                m_result = m_result && WriteName(valueName) && m_writer.Int64(value);
+                m_result = m_result && WriteName(visitArgs.m_fieldName) && m_writer.Int64(value);
             }
 
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::u64 value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZ::u64 value) override
             {
-                m_result = m_result && WriteName(valueName) && m_writer.Uint64(value);
+                m_result = m_result && WriteName(visitArgs.m_fieldName) && m_writer.Uint64(value);
             }
 
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, double value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, double value) override
             {
-                m_result = m_result && WriteName(valueName) && m_writer.Double(value);
+                m_result = m_result && WriteName(visitArgs.m_fieldName) && m_writer.Double(value);
             }
 
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZStd::string_view value) override
             {
-                m_result = m_result && WriteName(valueName) && m_writer.String(value.data(), aznumeric_caster(value.size()));
+                m_result = m_result && WriteName(visitArgs.m_fieldName) && m_writer.String(value.data(), aznumeric_caster(value.size()));
             }
 
             bool Finalize()
@@ -1404,25 +1401,32 @@ namespace AZ::SettingsRegistryMergeUtils
 
     bool IsPathAncestorDescendantOrEqual(AZStd::string_view candidatePath, AZStd::string_view inputPath)
     {
-        AZ::IO::PathView candidateView{ candidatePath, AZ::IO::PosixPathSeparator };
-        AZ::IO::PathView inputView{ inputPath, AZ::IO::PosixPathSeparator };
+        const AZ::IO::PathView candidateView{ candidatePath, AZ::IO::PosixPathSeparator };
+        const AZ::IO::PathView inputView{ inputPath, AZ::IO::PosixPathSeparator };
         return inputView.empty() || candidateView.IsRelativeTo(inputView) || inputView.IsRelativeTo(candidateView);
     }
 
+    bool IsPathDescendantOrEqual(AZStd::string_view candidatePath, AZStd::string_view inputPath)
+    {
+        const AZ::IO::PathView candidateView{ candidatePath, AZ::IO::PosixPathSeparator };
+        const AZ::IO::PathView inputView{ inputPath, AZ::IO::PosixPathSeparator };
+        return inputView.IsRelativeTo(candidateView);
+    }
+
     void VisitActiveGems(SettingsRegistryInterface& registry, const GemCallback& activeGemCallback)
     {
         using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
-        using Type = AZ::SettingsRegistryInterface::Type;
 
-        auto VisitGem = [&registry, &activeGemCallback](AZStd::string_view, AZStd::string_view gemName, Type)
+        auto VisitGem = [&registry, &activeGemCallback](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             // Lookup the Gem Path underneath the `ManifestGemsRootKey/<gem-name>` field
-            const auto gemPathKey = FixedValueString::format("%s/%.*s/Path", ManifestGemsRootKey, AZ_STRING_ARG(gemName));
+            const auto gemPathKey = FixedValueString::format("%s/%.*s/Path", ManifestGemsRootKey, AZ_STRING_ARG(visitArgs.m_fieldName));
 
             if (AZ::IO::FixedMaxPath gemPath; registry.Get(gemPath.Native(), gemPathKey))
             {
-                activeGemCallback(gemName, gemPath.Native());
+                activeGemCallback(visitArgs.m_fieldName, gemPath.Native());
             }
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
         SettingsRegistryVisitorUtils::VisitObject(registry, VisitGem, ActiveGemsRootKey);
     }
@@ -1455,12 +1459,11 @@ namespace AZ::SettingsRegistryMergeUtils
         gemManifestCallback(manifestObjectKey, manifestObjectName, manifestRootDirView.Native());
 
         // Visit children external subdirectories
-        using Type = AZ::SettingsRegistryInterface::Type;
         auto VisitExternalSubdirectories = [&gemManifestCallback, &manifestJsonRegistry, manifestRootDirView]
-        (AZStd::string_view externalSubdirectoryJsonPath, AZStd::string_view, Type)
+        (const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             if (FixedValueString externalSubdirectoryPath;
-                manifestJsonRegistry.Get(externalSubdirectoryPath, externalSubdirectoryJsonPath))
+                manifestJsonRegistry.Get(externalSubdirectoryPath, visitArgs.m_jsonKeyPath))
             {
                 auto gemManifestPath = (AZ::IO::FixedMaxPath(manifestRootDirView)
                     / externalSubdirectoryPath / Internal::GemJsonFilename).LexicallyNormal();
@@ -1469,6 +1472,7 @@ namespace AZ::SettingsRegistryMergeUtils
                     VisitManifestJson(gemManifestCallback, gemManifestPath.Native(), GemNameKey);
                 }
             }
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitArray(manifestJsonRegistry, VisitExternalSubdirectories, ExternalSubdirectoriesKey);

+ 6 - 0
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h

@@ -346,6 +346,12 @@ namespace AZ::SettingsRegistryMergeUtils
     //! IsPathAncestorDescendantOrEqual("/Amazon/AzCore/Bootstrap", "") = true
     bool IsPathAncestorDescendantOrEqual(AZStd::string_view candidatePath, AZStd::string_view inputPath);
 
+    //! Check if the supplied input path is a descendant or exactly equal to the candidate path
+    //! @param candidatePath Path which is being checked for the descendant relationship
+    //! @param inputPath Path which is checked to determine if it is equal or a descendant of the candidate path
+    //! @return true if the input path is an descendant or equal to the candidate path
+    bool IsPathDescendantOrEqual(AZStd::string_view candidatePath, AZStd::string_view inputPath);
+
     //! Callback signature which VisitActiveGems invokes for each Gem found underneath the ActiveGems root key
     //! @param gem_name value of "gem_name" field
     //! @param gemPath absolute path to the directory containing the gem.json

+ 23 - 12
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.cpp

@@ -18,8 +18,8 @@ namespace AZ::SettingsRegistryVisitorUtils
     {
     }
 
-    auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName,
-        VisitAction action, Type type) -> VisitResponse
+    auto FieldVisitor::Traverse(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
+        VisitAction action) -> VisitResponse
     {
         // A default response skip prevents visiting grand children(depth 2 or lower)
         VisitResponse visitResponse = VisitResponse::Skip;
@@ -28,10 +28,15 @@ namespace AZ::SettingsRegistryVisitorUtils
             // Invoke FieldVisitor override if the root path has been set
             if (m_rootPath.has_value())
             {
-                Visit(path, valueName, type);
+                // Only check the VisitResponse of Done to halt iteration
+                if (VisitResponse fieldResponse = Visit(visitArgs);
+                    fieldResponse == VisitResponse::Done)
+                {
+                    visitResponse = VisitResponse::Done;
+                }
             }
             // To make sure only the direct children are visited(depth 1)
-            // set the root path once and set the VisitReponsoe
+            // set the root path once and set the VisitResponse
             // to Continue to recurse into is fields
             if (!m_rootPath.has_value())
             {
@@ -39,13 +44,14 @@ namespace AZ::SettingsRegistryVisitorUtils
                 switch (m_visitFieldType)
                 {
                 case VisitFieldType::Array:
-                    visitableFieldType = type == Type::Array;
+                    visitableFieldType = visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Array;
                     break;
                 case VisitFieldType::Object:
-                    visitableFieldType = type == Type::Object;
+                    visitableFieldType = visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Object;
                     break;
                 case VisitFieldType::ArrayOrObject:
-                    visitableFieldType = type == Type::Array || type ==Type::Object;
+                    visitableFieldType = visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Array
+                        || visitArgs.m_type.m_type == AZ::SettingsRegistryInterface::Type::Object;
                     break;
                 default:
                     AZ_Error("FieldVisitor", false, "The field visitation type value is invalid");
@@ -54,7 +60,7 @@ namespace AZ::SettingsRegistryVisitorUtils
 
                 if (visitableFieldType)
                 {
-                    m_rootPath = path;
+                    m_rootPath = visitArgs.m_jsonKeyPath;
                     visitResponse = VisitResponse::Continue;
                 }
             }
@@ -64,13 +70,18 @@ namespace AZ::SettingsRegistryVisitorUtils
             // Invoke FieldVisitor override if the root path has been set
             if (m_rootPath.has_value())
             {
-                Visit(path, valueName, type);
+                // Only check the VisitResponse of Done to halt iteration
+                if (VisitResponse fieldResponse = Visit(visitArgs);
+                    fieldResponse == VisitResponse::Done)
+                {
+                    visitResponse = VisitResponse::Done;
+                }
             }
         }
         else if (action == VisitAction::End)
         {
             // Reset m_rootPath back to null when the root path has finished being visited
-            if (m_rootPath.has_value() && *m_rootPath == path)
+            if (m_rootPath.has_value() && *m_rootPath == visitArgs.m_jsonKeyPath)
             {
                 m_rootPath = AZStd::nullopt;
             }
@@ -104,9 +115,9 @@ namespace AZ::SettingsRegistryVisitorUtils
                 : m_visitCallback{ visitCallback }
             {}
 
-            void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override
+            AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) override
             {
-                m_visitCallback(path, fieldIndex, type);
+                return m_visitCallback(visitArgs);
             }
 
             const VisitorCallback& m_visitCallback;

+ 9 - 7
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.h

@@ -20,13 +20,15 @@ namespace AZ::SettingsRegistryVisitorUtils
     {
         using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
         using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
-        using Type = AZ::SettingsRegistryInterface::Type;
 
         FieldVisitor();
 
         // Bring the base class visitor functions into scope
         using AZ::SettingsRegistryInterface::Visitor::Visit;
-        virtual void Visit(AZStd::string_view path, AZStd::string_view fieldName, Type type) = 0;
+
+        //! @return VisitResponse of Done halts further iteration of sibling fields
+        //! all other VisitRepsonse values are ignored
+        virtual AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) = 0;
 
     protected:
         // VisitFieldType is used for filtering the type of referenced by the root path
@@ -38,8 +40,7 @@ namespace AZ::SettingsRegistryVisitorUtils
         };
         FieldVisitor(const VisitFieldType visitFieldType);
     private:
-        VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
-            VisitAction action, Type type) override;
+        VisitResponse Traverse(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, VisitAction action) override;
 
         VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject };
         AZStd::optional<AZ::SettingsRegistryInterface::FixedValueString> m_rootPath;
@@ -61,9 +62,10 @@ namespace AZ::SettingsRegistryVisitorUtils
         ObjectVisitor();
     };
 
-    //! Signature of callback funcition invoked when visiting an element of an array or object
-    using VisitorCallback = AZStd::function<void(AZStd::string_view path, AZStd::string_view fieldName,
-        AZ::SettingsRegistryInterface::Type)>;
+    //! Signature of callback function invoked when visiting an element of an array or object
+    //! @return VisitResponse of Done halts further iteration of sibling fields
+    //! all other VisitRepsonse values are ignored
+    using VisitorCallback = AZStd::function<AZ::SettingsRegistryInterface::VisitResponse(const AZ::SettingsRegistryInterface::VisitArgs&)>;
 
     //! Invokes the visitor callback for each element of either the array or object at @path
     //! If @path is not an array or object, then no elements are visited

+ 16 - 52
Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StreamerConfiguration_Windows.cpp

@@ -12,6 +12,7 @@
 #include <AzCore/IO/Streamer/StreamerConfiguration_Windows.h>
 #include <AzCore/Settings/SettingsRegistry.h>
 #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
 #include <AzCore/std/containers/unordered_map.h>
 #include <AzCore/StringFunc/StringFunc.h>
 
@@ -231,64 +232,27 @@ namespace AZ::IO
 
     }
 
-    static bool IsDriveUsed(AZStd::string_view driveId)
+    static bool IsDriveUsed(AZ::IO::PathView driveId)
     {
-        struct PathVisitor : SettingsRegistryInterface::Visitor
+        bool driveFound{};
+        auto IsDriveInUse = [&driveId, &driveFound](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
-            ~PathVisitor() override = default;
-
-            AZStd::string_view m_driveId;
-            bool m_firstObject = true;
-            bool m_found = false;
-
-            SettingsRegistryInterface::VisitResponse Traverse([[maybe_unused]] AZStd::string_view path,
-                [[maybe_unused]] AZStd::string_view valueName, [[maybe_unused]] SettingsRegistryInterface::VisitAction action,
-                [[maybe_unused]] SettingsRegistryInterface::Type type) override
+            AZ::IO::FixedMaxPath runtimePath;
+            if (visitArgs.m_registry.Get(runtimePath.Native(), visitArgs.m_jsonKeyPath) && runtimePath.RootName() == driveId)
             {
-                if (m_found)
-                {
-                    return SettingsRegistryInterface::VisitResponse::Done;
-                }
-
-                if (type == SettingsRegistryInterface::Type::Object)
-                {
-                    if (m_firstObject)
-                    {
-                        m_firstObject = false;
-                        return SettingsRegistryInterface::VisitResponse::Continue;
-                    }
-                    else
-                    {
-                        return SettingsRegistryInterface::VisitResponse::Skip;
-                    }
-                }
-
-                return type == SettingsRegistryInterface::Type::String ?
-                    SettingsRegistryInterface::VisitResponse::Continue : SettingsRegistryInterface::VisitResponse::Skip;
+                // Halt iteration if there exist O3DE is using a path from the drive
+                driveFound = true;
+                return AZ::SettingsRegistryInterface::VisitResponse::Done;
             }
 
-            using SettingsRegistryInterface::Visitor::Visit;
-            void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName,
-                [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
-            {
-                constexpr bool caseSensitive = false;
-                if (AZ::StringFunc::StartsWith(value, m_driveId, caseSensitive))
-                {
-                    m_found = true;
-                }
-            }
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
-        PathVisitor visitor;
-        visitor.m_driveId = driveId;
-        if (driveId.back() == AZ_CORRECT_FILESYSTEM_SEPARATOR || driveId.back() == AZ_WRONG_FILESYSTEM_SEPARATOR)
-        {
-            visitor.m_driveId.remove_suffix(1);
-        }
+
 
         auto settingsRegistry = SettingsRegistry::Get();
-        settingsRegistry->Visit(visitor, SettingsRegistryMergeUtils::FilePathsRootKey);
+        AZ::SettingsRegistryVisitorUtils::VisitObject(*settingsRegistry, IsDriveInUse, SettingsRegistryMergeUtils::FilePathsRootKey);
 
-        return visitor.m_found;
+        return driveFound;
     }
 
     static bool CollectHardwareInfo(HardwareInformation& hardwareInfo, bool addAllDrives, bool reportHardware)
@@ -301,6 +265,7 @@ namespace AZ::IO
             do
             {
                 UINT driveType = ::GetDriveTypeA(driveIt);
+                AZ::IO::PathView driveRootName = AZ::IO::PathView(driveIt).RootName();
                 // Only a selective set of devices that share similar behavior are supported, in particular
                 // drives that have magnetic or solid state storage. All types of buses (usb, sata, etc)
                 // are supported except network drives. If network support is needed it's better to use the
@@ -308,7 +273,7 @@ namespace AZ::IO
                 // on any platform for games, as games are expected to be downloaded or installed to storage.
                 if (driveType == DRIVE_FIXED || driveType == DRIVE_REMOVABLE || driveType == DRIVE_RAMDISK)
                 {
-                    if (!addAllDrives && !IsDriveUsed(driveIt))
+                    if (!addAllDrives && !IsDriveUsed(driveRootName))
                     {
                         if (reportHardware)
                         {
@@ -319,8 +284,7 @@ namespace AZ::IO
                     }
 
                     AZStd::string deviceName = R"(\\.\)";
-                    deviceName += driveIt;
-                    deviceName.erase(deviceName.length() - 1); // Erase the slash.
+                    deviceName += driveRootName.Native();
 
                     HANDLE deviceHandle = ::CreateFileA(
                         deviceName.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);

+ 3 - 3
Code/Framework/AzCore/Tests/Settings/SettingsRegistryMergeUtilsTests.cpp

@@ -527,13 +527,13 @@ tags=tools,renderer,metal)"
     TEST_P(SettingsRegistryGemVisitFixture, SettingsRegistryMergeUtils_AllManifestGems_AreInSettingsRegistry)
     {
         AZStd::vector<AZ::IO::Path> manifestGemPaths;
-        auto GetManifestGemPaths = [&manifestGemPaths, this](AZStd::string_view pathKey,
-            AZStd::string_view, AZ::SettingsRegistryInterface::Type)
+        auto GetManifestGemPaths = [&manifestGemPaths, this](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
             AZ::IO::Path gemPath;
-            EXPECT_TRUE(m_registry->Get(gemPath.Native(), FixedValueString(pathKey) + "/Path"));
+            EXPECT_TRUE(m_registry->Get(gemPath.Native(), FixedValueString(visitArgs.m_jsonKeyPath) + "/Path"));
             manifestGemPaths.push_back(gemPath.LexicallyRelative(m_testFolder.GetDirectory()));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         EXPECT_TRUE(AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry,

+ 22 - 22
Code/Framework/AzCore/Tests/Settings/SettingsRegistryTests.cpp

@@ -165,17 +165,17 @@ namespace SettingsRegistryTests
         void Visit(const AZStd::vector<RegistryEntry>& expected, AZStd::string_view path = "")
         {
             size_t counter = 0;
-            auto callback = [&expected, &counter](AZStd::string_view path, AZStd::string_view valueName,
-                AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type)
+            auto callback = [&expected, &counter](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
+                AZ::SettingsRegistryInterface::VisitAction action)
             {
                 EXPECT_LT(counter, expected.size());
                 if (counter < expected.size())
                 {
                     const RegistryEntry& entry = expected[counter];
-                    EXPECT_STREQ(entry.m_path.data(), path.data());
-                    EXPECT_STREQ(entry.m_valueName.data(), valueName.data());
+                    EXPECT_STREQ(entry.m_path.data(), visitArgs.m_jsonKeyPath.data());
+                    EXPECT_STREQ(entry.m_valueName.data(), visitArgs.m_fieldName.data());
                     EXPECT_EQ(entry.m_action, action);
-                    EXPECT_EQ(entry.m_type, type);
+                    EXPECT_EQ(entry.m_type, visitArgs.m_type);
                     counter++;
                     return entry.m_response;
                 }
@@ -426,10 +426,10 @@ namespace SettingsRegistryTests
             using AZ::SettingsRegistryInterface::Visitor::Visit;
 
             using ValueType [[maybe_unused]] = typename SettingsType<TypeParam>::ValueType;
-            void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, ValueType value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, ValueType value) override
             {
                 AZ::SettingsRegistryInterface::Type expectedType = SettingsType<TypeParam>::s_type;
-                EXPECT_EQ(expectedType, type);
+                EXPECT_EQ(expectedType, visitArgs.m_type);
                 SettingsType<TypeParam>::ExpectEq(SettingsType<TypeParam>::GetStoredValue(), value);
                 m_counter++;
             }
@@ -457,10 +457,10 @@ namespace SettingsRegistryTests
             using AZ::SettingsRegistryInterface::Visitor::Visit;
 
             using ValueType [[maybe_unused]] = typename SettingsType<TypeParam>::ValueType;
-            void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, ValueType value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, ValueType value) override
             {
                 AZ::SettingsRegistryInterface::Type expectedType = SettingsType<TypeParam>::s_type;
-                EXPECT_EQ(expectedType, type);
+                EXPECT_EQ(expectedType, visitArgs.m_type);
                 SettingsType<TypeParam>::ExpectEq(SettingsType<TypeParam>::GetStoredValue(), value);
                 m_counter++;
             }
@@ -487,9 +487,9 @@ namespace SettingsRegistryTests
         struct : public AZ::SettingsRegistryInterface::Visitor
         {
             using AZ::SettingsRegistryInterface::Visitor::Visit;
-            void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, AZ::s64 value) override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZ::s64 value) override
             {
-                EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, type);
+                EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, visitArgs.m_type);
                 AZ::u64 testValue = reinterpret_cast<AZ::u64&>(value);
                 EXPECT_EQ(expectedValue, testValue);
                 
@@ -516,17 +516,17 @@ namespace SettingsRegistryTests
 
         struct : public AZ::SettingsRegistryInterface::Visitor
         {
-            AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
-                AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type) override
+            AZ::SettingsRegistryInterface::VisitResponse Traverse(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
+                AZ::SettingsRegistryInterface::VisitAction) override
             {
-                EXPECT_TRUE(path.ends_with(valueName));
+                EXPECT_TRUE(visitArgs.m_jsonKeyPath.ends_with(visitArgs.m_fieldName));
                 return AZ::SettingsRegistryInterface::VisitResponse::Continue;
             }
 
             using AZ::SettingsRegistryInterface::Visitor::Visit;
-            void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type , AZStd::string_view)override
+            void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZStd::string_view)override
             {
-                EXPECT_TRUE(path.ends_with(valueName));
+                EXPECT_TRUE(visitArgs.m_jsonKeyPath.ends_with(visitArgs.m_fieldName));
             }
         } visitor;
 
@@ -1032,8 +1032,8 @@ namespace SettingsRegistryTests
 
     TEST_F(SettingsRegistryTest, VisitWithCallback_InvalidPath_ReturnsFalse)
     {
-        auto callback = [](AZStd::string_view, AZStd::string_view,
-            AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type)
+        auto callback = [](const AZ::SettingsRegistryInterface::VisitArgs&,
+            AZ::SettingsRegistryInterface::VisitAction)
         {
             return AZ::SettingsRegistryInterface::VisitResponse::Continue;
         };
@@ -1043,8 +1043,8 @@ namespace SettingsRegistryTests
 
     TEST_F(SettingsRegistryTest, VisitWithCallback_UnknownPath_ReturnsFalse)
     {
-        auto callback = [](AZStd::string_view, AZStd::string_view,
-            AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type)
+        auto callback = [](const AZ::SettingsRegistryInterface::VisitArgs&,
+            AZ::SettingsRegistryInterface::VisitAction)
         {
             return AZ::SettingsRegistryInterface::VisitResponse::Continue;
         };
@@ -1054,8 +1054,8 @@ namespace SettingsRegistryTests
 
     TEST_F(SettingsRegistryTest, VisitWithCallback_WithEmptyStringViewPath_DoesNotCrash)
     {
-        auto callback = [](AZStd::string_view, AZStd::string_view,
-            AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type)
+        auto callback = [](const AZ::SettingsRegistryInterface::VisitArgs&,
+            AZ::SettingsRegistryInterface::VisitAction)
         {
             return AZ::SettingsRegistryInterface::VisitResponse::Continue;
         };

+ 21 - 18
Code/Framework/AzCore/Tests/Settings/SettingsRegistryVisitorUtilsTests.cpp

@@ -19,9 +19,6 @@ namespace SettingsRegistryVisitorUtilsTests
     struct VisitCallbackParams
     {
         AZStd::string_view m_inputJsonDocument;
-        using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&,
-            const AZ::SettingsRegistryVisitorUtils::VisitorCallback&,
-            AZStd::string_view);
 
         static inline constexpr size_t MaxFieldCount = 10;
         using ObjectFields = AZStd::fixed_vector<AZStd::pair<AZStd::string_view, AZStd::string_view>, MaxFieldCount>;
@@ -59,11 +56,12 @@ namespace SettingsRegistryVisitorUtilsTests
         ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
         AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
-        auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
+        auto visitorCallback = [this, &testArrayFields](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             AZStd::string fieldValue;
-            EXPECT_TRUE(m_registry->Get(fieldValue, path));
+            EXPECT_TRUE(m_registry->Get(fieldValue, visitArgs.m_jsonKeyPath));
             testArrayFields.emplace_back(AZStd::move(fieldValue));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array");
@@ -80,11 +78,12 @@ namespace SettingsRegistryVisitorUtilsTests
         ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
         AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
-        auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
+        auto visitorCallback = [this, &testObjectFields](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             AZStd::string fieldValue;
-            EXPECT_TRUE(m_registry->Get(fieldValue, path));
-            testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
+            EXPECT_TRUE(m_registry->Get(fieldValue, visitArgs.m_jsonKeyPath));
+            testObjectFields.emplace_back(visitArgs.m_fieldName, AZStd::move(fieldValue));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object");
@@ -101,11 +100,12 @@ namespace SettingsRegistryVisitorUtilsTests
         ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
         AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
-        auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
+        auto visitorCallback = [this, &testArrayFields](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             AZStd::string fieldValue;
-            EXPECT_TRUE(m_registry->Get(fieldValue, path));
+            EXPECT_TRUE(m_registry->Get(fieldValue, visitArgs.m_jsonKeyPath));
             testArrayFields.emplace_back(AZStd::move(fieldValue));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array");
@@ -122,11 +122,12 @@ namespace SettingsRegistryVisitorUtilsTests
         ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
         AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
-        auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
+        auto visitorCallback = [this, &testArrayFields](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             AZStd::string fieldValue;
-            EXPECT_TRUE(m_registry->Get(fieldValue, path));
+            EXPECT_TRUE(m_registry->Get(fieldValue, visitArgs.m_jsonKeyPath));
             testArrayFields.emplace_back(AZStd::move(fieldValue));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object");
@@ -141,11 +142,12 @@ namespace SettingsRegistryVisitorUtilsTests
         ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
         AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
-        auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
+        auto visitorCallback = [this, &testObjectFields](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             AZStd::string fieldValue;
-            EXPECT_TRUE(m_registry->Get(fieldValue, path));
-            testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
+            EXPECT_TRUE(m_registry->Get(fieldValue, visitArgs.m_jsonKeyPath));
+            testObjectFields.emplace_back(visitArgs.m_fieldName, AZStd::move(fieldValue));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array");
@@ -160,11 +162,12 @@ namespace SettingsRegistryVisitorUtilsTests
         ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
         AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
-        auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
+        auto visitorCallback = [this, &testObjectFields](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
         {
             AZStd::string fieldValue;
-            EXPECT_TRUE(m_registry->Get(fieldValue, path));
-            testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
+            EXPECT_TRUE(m_registry->Get(fieldValue, visitArgs.m_jsonKeyPath));
+            testObjectFields.emplace_back(visitArgs.m_fieldName, AZStd::move(fieldValue));
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object");

+ 20 - 50
Code/Framework/AzFramework/AzFramework/Application/Application.cpp

@@ -585,55 +585,27 @@ namespace AzFramework
     }
 
     struct DeprecatedAliasesKeyVisitor
-        : AZ::SettingsRegistryInterface::Visitor
+        : AZ::SettingsRegistryVisitorUtils::ArrayVisitor
     {
-        using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
-        using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
-        using Type = AZ::SettingsRegistryInterface::Type;
+        using VisitArgs = AZ::SettingsRegistryInterface::VisitArgs;
 
         using AZ::SettingsRegistryInterface::Visitor::Visit;
 
-        VisitResponse Traverse(AZStd::string_view path, AZStd::string_view,
-            VisitAction action, Type type) override
+        AZ::SettingsRegistryInterface::VisitResponse Visit(const VisitArgs& visitArgs) override
         {
-            if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
-            {
-                if (type == AZ::SettingsRegistryInterface::Type::Array)
-                {
-                    m_parentArrayPath = path;
-                }
-
-                // Strip off last path segment from json path and check if is a child element of the array
-                if (AZ::StringFunc::TokenizeLast(path, '/');
-                    m_parentArrayPath == path)
-                {
-                    m_aliases.emplace_back();
-                }
-            }
-            else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
+            using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+            AliasPair aliasPair;
+            const auto oldAliasKeyPath = FixedValueString::format("%.*s/%s", AZ_STRING_ARG(visitArgs.m_jsonKeyPath),
+                ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey);
+            const auto newAliasKeyPath = FixedValueString::format("%.*s/%s", AZ_STRING_ARG(visitArgs.m_jsonKeyPath),
+                ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey);
+            if (visitArgs.m_registry.Get(aliasPair.m_oldAlias, oldAliasKeyPath)
+                && visitArgs.m_registry.Get(aliasPair.m_newAlias, newAliasKeyPath))
             {
-                if (type == AZ::SettingsRegistryInterface::Type::Array)
-                {
-                    m_parentArrayPath = AZStd::string{};
-                }
+                m_aliases.emplace_back(AZStd::move(aliasPair));
             }
 
-            return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-        }
-
-        void Visit(AZStd::string_view, AZStd::string_view valueName, Type, AZStd::string_view value) override
-        {
-            if (!m_aliases.empty())
-            {
-                if (valueName == ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey)
-                {
-                    m_aliases.back().m_oldAlias = value;
-                }
-                else if (valueName == ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey)
-                {
-                    m_aliases.back().m_newAlias = value;
-                }
-            }
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         }
 
         struct AliasPair
@@ -642,9 +614,6 @@ namespace AzFramework
             AZStd::string m_newAlias;
         };
         AZStd::vector<AliasPair> m_aliases;
-
-    private:
-        AZStd::string m_parentArrayPath;
     };
 
     static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase)
@@ -739,8 +708,8 @@ namespace AzFramework
             fileIoBase->SetAlias("@log@", projectLogPath.c_str());
             fileIoBase->CreatePath(projectLogPath.c_str());
 
-            DeprecatedAliasesKeyVisitor visitor;
-            if (m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot))
+            if (DeprecatedAliasesKeyVisitor visitor;
+                m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot))
             {
                 for (const auto& [oldAlias, newAlias] : visitor.m_aliases)
                 {
@@ -750,7 +719,6 @@ namespace AzFramework
 
             // The following section sets the @gemroot:<gem-name>@ alias for
             // every loaded gem
-            using Type = AZ::SettingsRegistryInterface::Type;
             using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
             auto AddGemAlias = [&fileIoBase](AZStd::string_view gemName, AZStd::string_view gemRootPath)
             {
@@ -762,15 +730,17 @@ namespace AzFramework
 
             // Load any Filesystem aliases from the SettingsRegistry
             auto SetAliasesFromSettingsRegistry = [&fileIoBase, &settingsRegistry = *m_settingsRegistry]
-                (AZStd::string_view aliasJsonPath, AZStd::string_view aliasKey, Type)
+                (const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
             {
-                if (AZ::IO::FixedMaxPath aliasPath; settingsRegistry.Get(aliasPath.Native(), aliasJsonPath))
+                if (AZ::IO::FixedMaxPath aliasPath; settingsRegistry.Get(aliasPath.Native(), visitArgs.m_jsonKeyPath))
                 {
                     if (AZ::IO::SystemFile::Exists(aliasPath.c_str()))
                     {
-                        fileIoBase->SetAlias(FixedValueString(aliasKey).c_str(), aliasPath.c_str());
+                        fileIoBase->SetAlias(FixedValueString(visitArgs.m_fieldName).c_str(), aliasPath.c_str());
                     }
                 }
+
+                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
             };
             AZ::SettingsRegistryVisitorUtils::VisitObject(*m_settingsRegistry, SetAliasesFromSettingsRegistry,
                 ApplicationInternal::FilesystemAliasesRoot);

+ 6 - 9
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/SettingsRegistryAdapter.cpp

@@ -133,7 +133,7 @@ namespace AZ::DocumentPropertyEditor
                     m_inEdit = true;
                     settingsRegistry.Set(keyPath, static_cast<AZ::u64>(domValue));
                     m_inEdit = false;
-                    return AZ::Dom::Value(domValue);
+                    return AZ::Dom::Value();
                 }
                 return {};
             };
@@ -205,7 +205,7 @@ namespace AZ::DocumentPropertyEditor
     }
 
     bool SettingsRegistryAdapter::BuildField(
-        AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type type)
+        AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::SettingsType type)
     {
         struct ScopedAdapterBuilderRowCreation
         {
@@ -230,11 +230,7 @@ namespace AZ::DocumentPropertyEditor
         }
         else if (type == AZ::SettingsRegistryInterface::Type::Integer)
         {
-            using SettingsType = AZ::SettingsRegistryInterface::SettingsType;
-
-            AZ::SettingsRegistryInterface& settingsRegistry = m_originTracker->GetSettingsRegistry();
-            const SettingsType settingsType = settingsRegistry.GetType(path);
-            if (settingsType.m_signedness == AZ::SettingsRegistryInterface::Signedness::Signed)
+            if (type.m_signedness == AZ::SettingsRegistryInterface::Signedness::Signed)
             {
                 BuildInt64(path);
             }
@@ -253,9 +249,10 @@ namespace AZ::DocumentPropertyEditor
         }
         else if (type == AZ::SettingsRegistryInterface::Type::Object || type == AZ::SettingsRegistryInterface::Type::Array)
         {
-            auto visitorCallback = [this](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type type)
+            auto visitorCallback = [this](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
             {
-                BuildField(path, fieldName, type);
+                BuildField(visitArgs.m_jsonKeyPath, visitArgs.m_fieldName, visitArgs.m_type);
+                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
             };
             AZ::SettingsRegistryInterface& settingsRegistry = m_originTracker->GetSettingsRegistry();
             AZ::SettingsRegistryVisitorUtils::VisitField(settingsRegistry, visitorCallback, path);

+ 1 - 1
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/SettingsRegistryAdapter.h

@@ -29,7 +29,7 @@ namespace AZ::DocumentPropertyEditor
 
         // Recurses through the Settings Registry associated with the supplied Settings Registry Origin Tracker
         // populates PropertyEditorNodes
-        bool BuildField(AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type type);
+        bool BuildField(AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::SettingsType type);
 
         SettingsRegistryOriginTracker* GetSettingsRegistryOriginTracker();
         const SettingsRegistryOriginTracker* GetSettingsRegistryOriginTracker() const;

+ 11 - 9
Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp

@@ -27,35 +27,37 @@ namespace AzFramework
     bool GetGemsInfo(AZStd::vector<GemInfo>& gemInfoList, AZ::SettingsRegistryInterface& settingsRegistry)
     {
         using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
-        using Type = AZ::SettingsRegistryInterface::Type;
 
         auto GemSettingsVisitor = [&settingsRegistry, &gemInfoList]
-        (AZStd::string_view gemObjectKeyPath, AZStd::string_view gemName, AZ::SettingsRegistryInterface::Type)
+        (const AZ::SettingsRegistryInterface::VisitArgs& gemVisitArgs)
         {
-            auto FindGemInfoByName = [gemName](const GemInfo& gemInfo)
+            auto FindGemInfoByName = [&gemVisitArgs](const GemInfo& gemInfo)
             {
-                return gemName == gemInfo.m_gemName;
+                return gemVisitArgs.m_fieldName == gemInfo.m_gemName;
             };
             auto gemInfoFoundIter = AZStd::ranges::find_if(gemInfoList, FindGemInfoByName);
-            GemInfo& gemInfo = gemInfoFoundIter != gemInfoList.end() ? *gemInfoFoundIter : gemInfoList.emplace_back(gemName);
+            GemInfo& gemInfo = gemInfoFoundIter != gemInfoList.end() ? *gemInfoFoundIter : gemInfoList.emplace_back(gemVisitArgs.m_fieldName);
 
             // Read the Gem Target Name into target Name field
-            auto VisitGemTargets = [&gemInfo](AZStd::string_view, AZStd::string_view fieldName, Type)
+            auto VisitGemTargets = [&gemInfo](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
             {
                 // Assume the fieldName is the name of the target in this case
-                gemInfo.m_gemTargetNames.emplace_back(fieldName);
+                gemInfo.m_gemTargetNames.emplace_back(visitArgs.m_fieldName);
+                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
             };
             AZ::SettingsRegistryVisitorUtils::VisitObject(settingsRegistry, VisitGemTargets,
-                FixedValueString::format("%.*s/Targets", AZ_STRING_ARG(gemObjectKeyPath)));
+                FixedValueString::format("%.*s/Targets", AZ_STRING_ARG(gemVisitArgs.m_jsonKeyPath)));
 
             // Visit the "SourcePath" array fields of the gem to
             // populate the Gem Absolute Source Paths array
             const auto gemPathKey = FixedValueString::format("%s/%.*s/Path",
-                AZ::SettingsRegistryMergeUtils::ManifestGemsRootKey, AZ_STRING_ARG(gemName));
+                AZ::SettingsRegistryMergeUtils::ManifestGemsRootKey, AZ_STRING_ARG(gemVisitArgs.m_fieldName));
             if (AZ::IO::Path gemRootPath; settingsRegistry.Get(gemRootPath.Native(), gemPathKey))
             {
                 gemInfo.m_absoluteSourcePaths.emplace_back(gemRootPath);
             }
+
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         };
 
         AZ::SettingsRegistryVisitorUtils::VisitObject(settingsRegistry, GemSettingsVisitor,

+ 16 - 18
Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp

@@ -98,27 +98,25 @@ namespace AzToolsFramework::AssetUtils
         : AZ::SettingsRegistryInterface::Visitor
     {
         using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-        AZStd::vector<AZStd::string> m_enabledPlatforms;
-    };
-
-    void EnabledPlatformsVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
-    {
-        if (value == "enabled")
-        {
-            m_enabledPlatforms.emplace_back(valueName);
-        }
-        else if (value == "disabled")
+        void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZStd::string_view value) override
         {
-            auto platformEntrySearch = [&valueName](AZStd::string_view platformEntry)
+            if (value == "enabled")
             {
-                return valueName == platformEntry;
-            };
-            auto removeIt = AZStd::remove_if(m_enabledPlatforms.begin(), m_enabledPlatforms.end(), platformEntrySearch);
-            m_enabledPlatforms.erase(removeIt, m_enabledPlatforms.end());
+                m_enabledPlatforms.emplace_back(visitArgs.m_fieldName);
+            }
+            else if (value == "disabled")
+            {
+                auto platformEntrySearch = [&visitArgs](AZStd::string_view platformEntry)
+                {
+                    return visitArgs.m_fieldName == platformEntry;
+                };
+                auto removeIt = AZStd::remove_if(m_enabledPlatforms.begin(), m_enabledPlatforms.end(), platformEntrySearch);
+                m_enabledPlatforms.erase(removeIt, m_enabledPlatforms.end());
+            }
         }
-    }
+
+        AZStd::vector<AZStd::string> m_enabledPlatforms;
+    };
 
     void ReadEnabledPlatformsFromSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry,
         AZStd::vector<AZStd::string>& enabledPlatforms)

+ 65 - 70
Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/LocalViewBookmarkLoader.cpp

@@ -9,6 +9,7 @@
 #include <API/ToolsApplicationAPI.h>
 #include <AzCore/Settings/SettingsRegistry.h>
 #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
 #include <AzCore/StringFunc/StringFunc.h>
 #include <AzCore/Utils/Utils.h>
 #include <AzFramework/Viewport/CameraInput.h>
@@ -27,41 +28,19 @@ namespace AzToolsFramework
     static constexpr const char* LocalBookmarksKey = "LocalBookmarks";
     static constexpr const char* LastKnownLocationKey = "LastKnownLocation";
 
-    struct ViewBookmarkVisitor : AZ::SettingsRegistryInterface::Visitor
+    struct ViewBookmarkVisitor
+        : AZ::SettingsRegistryInterface::Visitor
     {
         ViewBookmarkVisitor()
             : m_viewBookmarksKey{ ViewBookmarksRegistryPath } {};
 
-        AZ::SettingsRegistryInterface::VisitResponse Traverse(
-            AZStd::string_view path,
-            [[maybe_unused]] AZStd::string_view valueName,
-            AZ::SettingsRegistryInterface::VisitAction action,
-            [[maybe_unused]] AZ::SettingsRegistryInterface::Type type) override
-        {
-            if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
-            {
-                // strip off the last json pointer key from the path and if it matches the view bookmark key then add an entry
-                // to the view bookmark map
-                AZStd::optional<AZStd::string_view> localBookmarksId = AZ::StringFunc::TokenizeLast(path, "/");
-                if (path == m_viewBookmarksKey && localBookmarksId.has_value() && !localBookmarksId->empty())
-                {
-                    if (const auto existingBookmarkEntry = m_bookmarkMap.find(localBookmarksId.value());
-                        existingBookmarkEntry == m_bookmarkMap.end())
-                    {
-                        m_bookmarkMap.insert(AZStd::make_pair(localBookmarksId.value(), AZStd::vector<ViewBookmark>{}));
-                    }
-                }
-            }
-
-            return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-        }
-
         using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view valueIndex, AZ::SettingsRegistryInterface::Type, double value) override
+        void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, double value) override
         {
-            AZ::StringFunc::TokenizeLast(path, "/");
-            AZStd::optional<AZStd::string_view> dataType = AZ::StringFunc::TokenizeLast(path, "/");
-            AZStd::optional<AZStd::string_view> bookmarkIndexStr = AZ::StringFunc::TokenizeLast(path, "/");
+            AZStd::string_view jsonKeyPath = visitArgs.m_jsonKeyPath;
+            AZ::StringFunc::TokenizeLast(jsonKeyPath, "/");
+            AZStd::optional<AZStd::string_view> dataType = AZ::StringFunc::TokenizeLast(jsonKeyPath, "/");
+            AZStd::optional<AZStd::string_view> bookmarkIndexStr = AZ::StringFunc::TokenizeLast(jsonKeyPath, "/");
             AZStd::optional<AZStd::string_view> bookmarkType;
             if (bookmarkIndexStr == LastKnownLocationKey)
             {
@@ -70,11 +49,11 @@ namespace AzToolsFramework
             else
             {
                 // differentiate between local Bookmarks and LastKnownLocation
-                bookmarkType = AZ::StringFunc::TokenizeLast(path, "/");
+                bookmarkType = AZ::StringFunc::TokenizeLast(jsonKeyPath, "/");
             }
 
-            if (AZStd::optional<AZStd::string_view> localBookmarksId = AZ::StringFunc::TokenizeLast(path, "/");
-                localBookmarksId.has_value() && path == m_viewBookmarksKey && !localBookmarksId->empty())
+            if (AZStd::optional<AZStd::string_view> localBookmarksId = AZ::StringFunc::TokenizeLast(jsonKeyPath, "/");
+                localBookmarksId.has_value() && jsonKeyPath == m_viewBookmarksKey && !localBookmarksId->empty())
             {
                 auto setVec3Fn = [value](AZ::Vector3& inout, int currentIndex)
                 {
@@ -103,7 +82,7 @@ namespace AzToolsFramework
                         m_lastKnownLocation.emplace(ViewBookmark{});
                     }
 
-                    const int currentIndex = AZStd::stoi(AZStd::string(valueIndex));
+                    const int currentIndex = AZStd::stoi(AZStd::string(visitArgs.m_fieldName));
                     if (dataType == "Position")
                     {
                         setVec3Fn(m_lastKnownLocation->m_position, currentIndex);
@@ -121,7 +100,7 @@ namespace AzToolsFramework
                         AZStd::vector<ViewBookmark>& bookmarks = existingBookmarkEntry->second;
                         // if it is the first bookmark and it is the Position data it means it is the first one
                         // and we have to create the Bookmark.
-                        if (valueIndex == "0" && dataType == "Position")
+                        if (visitArgs.m_fieldName == "0" && dataType == "Position")
                         {
                             ViewBookmark bookmark;
                             bookmark.m_position.SetX(aznumeric_cast<float>(value));
@@ -132,7 +111,7 @@ namespace AzToolsFramework
                             const int bookmarkIndex = AZStd::stoi(AZStd::string(bookmarkIndexStr->data()));
                             AZ_Assert(bookmarkIndex >= 0 && bookmarkIndex < bookmarks.size(), "Bookmark index is out of bounds");
                             ViewBookmark& bookmark = bookmarks[bookmarkIndex];
-                            const int currentIndex = AZStd::stoi(AZStd::string(valueIndex));
+                            const int currentIndex = AZStd::stoi(AZStd::string(visitArgs.m_fieldName));
 
                             if (dataType == "Position")
                             {
@@ -146,20 +125,20 @@ namespace AzToolsFramework
                     }
                 }
             }
-        };
+        }
 
         AZ::SettingsRegistryInterface::FixedValueString m_viewBookmarksKey;
         AZStd::unordered_map<AZStd::string, AZStd::vector<ViewBookmark>> m_bookmarkMap;
         AZStd::optional<ViewBookmark> m_lastKnownLocation;
     };
 
-    static AZ::IO::FixedMaxPath LocalBookmarkFilePath(const AZStd::string& bookmarkFileName)
+    static AZ::IO::FixedMaxPath LocalBookmarkFilePath(const AZ::IO::PathView& bookmarkFileName)
     {
         return AZ::IO::FixedMaxPath(AZ::Utils::GetProjectPath()) / "user/Registry/ViewBookmarks/" / bookmarkFileName;
     }
 
     // returns true if the file already existed, false otherwise
-    static bool EnsureLocalBookmarkFileExists(const AZStd::string& bookmarkFileName)
+    static bool EnsureLocalBookmarkFileExists(const AZ::IO::PathView& bookmarkFileName)
     {
         const auto localBookmarkFilePath = LocalBookmarkFilePath(bookmarkFileName);
 
@@ -174,22 +153,22 @@ namespace AzToolsFramework
         return outputFile.Length() > 0;
     }
 
-    static AZStd::string LocalViewBookmarkSetRegPath(const AZStd::string& bookmarkFileName)
+    static AZStd::string LocalViewBookmarkSetRegPath(const AZ::IO::PathView& bookmarkFileName)
     {
-        return AZStd::string::format("/%s/LocalBookmarks", bookmarkFileName.c_str());
+        return AZStd::string::format("/%.*s/LocalBookmarks", AZ_STRING_ARG(bookmarkFileName.Native()));
     }
 
-    static AZStd::string LocalViewBookmarkSetRegPathAtIndex(const AZStd::string& bookmarkFileName, const int index)
+    static AZStd::string LocalViewBookmarkSetRegPathAtIndex(const AZ::IO::PathView& bookmarkFileName, const int index)
     {
         return AZStd::string::format("%s/%i", LocalViewBookmarkSetRegPath(bookmarkFileName).c_str(), index);
     }
 
-    static AZStd::string LastKnownLocationSetRegPath(const AZStd::string& bookmarkFileName)
+    static AZStd::string LastKnownLocationSetRegPath(const AZ::IO::PathView& bookmarkFileName)
     {
-        return AZStd::string::format("/%s/LastKnownLocation", bookmarkFileName.c_str());
+        return AZStd::string::format("/%.*s/LastKnownLocation", AZ_STRING_ARG(bookmarkFileName.Native()));
     }
 
-    static AZStd::string GenerateBookmarkFileName()
+    static AZ::IO::Path GenerateBookmarkFileName()
     {
         auto* prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
         AZ_Assert(prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface is not found.");
@@ -197,7 +176,7 @@ namespace AzToolsFramework
 
         if (rootPrefabTemplateId == Prefab::InvalidTemplateId)
         {
-            return AZStd::string();
+            return {};
         }
 
         auto* prefabSystemComponent = AZ::Interface<Prefab::PrefabSystemComponentInterface>::Get();
@@ -208,7 +187,7 @@ namespace AzToolsFramework
             "Check that it is being correctly initialized.");
 
         const auto prefabTemplate = prefabSystemComponent->FindTemplate(rootPrefabTemplateId);
-        const AZStd::string prefabTemplateName = prefabTemplate->get().GetFilePath().Filename().Stem().Native();
+        const AZ::IO::Path prefabTemplateName = prefabTemplate->get().GetFilePath().Filename().Stem();
 
         // to generate the file name in which we will store the view Bookmarks we use the name of the prefab + the timestamp
         // e.g. LevelName_1639763579377.setreg
@@ -216,11 +195,11 @@ namespace AzToolsFramework
         return AZStd::string::format("%s_%llu.setreg", prefabTemplateName.c_str(), now.time_since_epoch().count());
     }
 
-    static AZ::SettingsRegistryMergeUtils::DumperSettings CreateDumperSettings(const AZStd::string& bookmarkKey)
+    static AZ::SettingsRegistryMergeUtils::DumperSettings CreateDumperSettings(AZStd::string_view bookmarkKey)
     {
         AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
         dumperSettings.m_prettifyOutput = true;
-        dumperSettings.m_includeFilter = [bookmarkKey](AZStd::string_view path)
+        dumperSettings.m_includeFilter = [bookmarkKey = AZStd::string(bookmarkKey)](AZStd::string_view path)
         {
             AZStd::string_view o3dePrefixPath(bookmarkKey);
             return o3dePrefixPath.starts_with(path.substr(0, o3dePrefixPath.size()));
@@ -233,8 +212,8 @@ namespace AzToolsFramework
         AZ::Interface<ViewBookmarkInterface>::Register(this);
         AZ::Interface<ViewBookmarkPersistInterface>::Register(this);
 
-        m_streamWriteFn = [](const AZStd::string& localBookmarksFileName, const AZStd::string stringBuffer,
-                             AZStd::function<bool(AZ::IO::GenericStream&, const AZStd::string&)> writeFn)
+        m_streamWriteFn = [](AZ::IO::PathView localBookmarksFileName, AZStd::string_view stringBuffer,
+                             AZStd::function<bool(AZ::IO::GenericStream&, AZStd::string_view)> writeFn)
         {
             constexpr auto configurationMode =
                 AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
@@ -255,7 +234,7 @@ namespace AzToolsFramework
             return saved;
         };
 
-        m_streamReadFn = [](const AZStd::string localBookmarksFileName)
+        m_streamReadFn = [](const AZ::IO::PathView localBookmarksFileName)
         {
             const auto localBookmarkFilePath = LocalBookmarkFilePath(localBookmarksFileName);
             const auto fileSize = AZ::IO::SystemFile::Length(localBookmarkFilePath.c_str());
@@ -268,7 +247,7 @@ namespace AzToolsFramework
             return buffer;
         };
 
-        m_fileExistsFn = [](const AZStd::string& localBookmarksFileName)
+        m_fileExistsFn = [](const AZ::IO::PathView& localBookmarksFileName)
         {
             return EnsureLocalBookmarkFileExists(localBookmarksFileName);
         };
@@ -280,7 +259,7 @@ namespace AzToolsFramework
         AZ::Interface<ViewBookmarkInterface>::Unregister(this);
     }
 
-    void LocalViewBookmarkLoader::WriteViewBookmarksSettingsRegistryToFile(const AZStd::string& localBookmarksFileName)
+    void LocalViewBookmarkLoader::WriteViewBookmarksSettingsRegistryToFile(const AZ::IO::PathView& localBookmarksFileName)
     {
         auto registry = AZ::SettingsRegistry::Get();
         if (!registry)
@@ -289,32 +268,34 @@ namespace AzToolsFramework
             return;
         }
 
-        const AZStd::string bookmarkKey = "/" + localBookmarksFileName;
+        using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+        auto bookmarkKey = FixedValueString("/");
+        bookmarkKey += localBookmarksFileName.Native();
 
         AZStd::string stringBuffer;
         AZ::IO::ByteContainerStream stringStream(&stringBuffer);
         if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "", stringStream, CreateDumperSettings(bookmarkKey)))
         {
             AZ_Error(
-                "LocalViewBookmarkLoader", false, R"(Unable to dump SettingsRegistry to stream "%s"\n)", localBookmarksFileName.c_str());
+                "LocalViewBookmarkLoader", false, R"(Unable to dump SettingsRegistry to stream "%.*s"\n)", AZ_STRING_ARG(localBookmarksFileName.Native()));
             return;
         }
 
         [[maybe_unused]] const bool saved = m_streamWriteFn(
             localBookmarksFileName, stringBuffer,
-            [this](AZ::IO::GenericStream& genericStream, const AZStd::string& stringBuffer)
+            [this](AZ::IO::GenericStream& genericStream, AZStd::string_view stringBuffer)
             {
                 return Write(genericStream, stringBuffer);
             });
 
         AZ_Warning(
-            "LocalViewBookmarkLoader", saved, R"(Unable to save Local View Bookmark file to path "%s"\n)", localBookmarksFileName.c_str());
+            "LocalViewBookmarkLoader", saved, R"(Unable to save Local View Bookmark file to path "%.*s"\n)", AZ_STRING_ARG(localBookmarksFileName.Native()));
 
         // once written to the desired file, remove the key from the settings registry
-        registry->Remove(bookmarkKey + "/");
+        registry->Remove(bookmarkKey);
     }
 
-    bool LocalViewBookmarkLoader::Write(AZ::IO::GenericStream& genericStream, const AZStd::string& stringBuffer)
+    bool LocalViewBookmarkLoader::Write(AZ::IO::GenericStream& genericStream, AZStd::string_view stringBuffer)
     {
         return genericStream.Write(stringBuffer.size(), stringBuffer.data()) == stringBuffer.size();
     }
@@ -341,7 +322,7 @@ namespace AzToolsFramework
             return false;
         }
 
-        const auto& localBookmarksFileName = bookmarkComponent->GetLocalBookmarksFileName();
+        const AZ::IO::PathView localBookmarksFileName(bookmarkComponent->GetLocalBookmarksFileName());
         ReadViewBookmarksSettingsRegistryFromFile(localBookmarksFileName);
 
         // write to the settings registry
@@ -368,7 +349,7 @@ namespace AzToolsFramework
             return false;
         }
 
-        const auto localBookmarksFileName = bookmarkComponent->GetLocalBookmarksFileName();
+        const AZ::IO::PathView localBookmarksFileName(bookmarkComponent->GetLocalBookmarksFileName());
 
         bool success = false;
         if (auto registry = AZ::SettingsRegistry::Get())
@@ -392,7 +373,7 @@ namespace AzToolsFramework
         return success;
     }
 
-    bool LocalViewBookmarkLoader::ReadViewBookmarksSettingsRegistryFromFile(const AZStd::string& localBookmarksFileName)
+    bool LocalViewBookmarkLoader::ReadViewBookmarksSettingsRegistryFromFile(const AZ::IO::PathView& localBookmarksFileName)
     {
         auto registry = AZ::SettingsRegistry::Get();
         if (!registry)
@@ -406,13 +387,27 @@ namespace AzToolsFramework
                 m_streamReadFn(localBookmarksFileName), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ViewBookmarksRegistryPath))
         {
             ViewBookmarkVisitor viewBookmarkVisitor;
-            const bool visitedViewBookmarks = registry->Visit(
-                viewBookmarkVisitor, AZStd::string::format("%s/%s", ViewBookmarksRegistryPath, localBookmarksFileName.c_str()));
+            auto VisitBookmarkKey = [&registry, &viewBookmarkVisitor](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
+            {
+                // The field name is the bookmarkId
+                AZStd::string_view localBookmarksId = visitArgs.m_fieldName;
+                if (const auto existingBookmarkEntry = viewBookmarkVisitor.m_bookmarkMap.find(localBookmarksId);
+                    existingBookmarkEntry == viewBookmarkVisitor.m_bookmarkMap.end())
+                {
+                    viewBookmarkVisitor.m_bookmarkMap.emplace(localBookmarksId, AZStd::vector<ViewBookmark>{});
+                    // Visit each localBookmarkId path
+                    registry->Visit(viewBookmarkVisitor, visitArgs.m_jsonKeyPath);
+
+                }
+                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
+            };
+
+            const bool visitedViewBookmarks = AZ::SettingsRegistryVisitorUtils::VisitObject(*registry, VisitBookmarkKey, ViewBookmarksRegistryPath);
 
             if (visitedViewBookmarks)
             {
                 // update local cache
-                m_localBookmarks = viewBookmarkVisitor.m_bookmarkMap.at(localBookmarksFileName);
+                m_localBookmarks = viewBookmarkVisitor.m_bookmarkMap[localBookmarksFileName.Native()];
                 m_lastKnownLocation = viewBookmarkVisitor.m_lastKnownLocation;
 
                 // update in-memory settings registry
@@ -442,7 +437,7 @@ namespace AzToolsFramework
             return AZStd::nullopt;
         }
 
-        ReadViewBookmarksSettingsRegistryFromFile(bookmarkComponent->GetLocalBookmarksFileName());
+        ReadViewBookmarksSettingsRegistryFromFile(AZ::IO::PathView(bookmarkComponent->GetLocalBookmarksFileName()));
 
         if (index >= 0 && index < m_localBookmarks.size())
         {
@@ -467,7 +462,7 @@ namespace AzToolsFramework
             return false;
         }
 
-        const auto& localBookmarksFileName = bookmarkComponent->GetLocalBookmarksFileName();
+        const AZ::IO::PathView localBookmarksFileName(bookmarkComponent->GetLocalBookmarksFileName());
 
         bool success = false;
         if (auto registry = AZ::SettingsRegistry::Get())
@@ -496,7 +491,7 @@ namespace AzToolsFramework
             return AZStd::nullopt;
         }
 
-        ReadViewBookmarksSettingsRegistryFromFile(bookmarkComponent->GetLocalBookmarksFileName());
+        ReadViewBookmarksSettingsRegistryFromFile(AZ::IO::PathView(bookmarkComponent->GetLocalBookmarksFileName()));
 
         return m_lastKnownLocation;
     }
@@ -540,10 +535,10 @@ namespace AzToolsFramework
         // if the field is empty, we don't have a file linked to the prefab, so we create one and we save it in the component
         if (bookmarkComponent->GetLocalBookmarksFileName().empty())
         {
-            bookmarkComponent->SetLocalBookmarksFileName(GenerateBookmarkFileName());
+            bookmarkComponent->SetLocalBookmarksFileName(GenerateBookmarkFileName().Native());
         }
 
-        if (const auto localBookmarksFileName = bookmarkComponent->GetLocalBookmarksFileName(); !m_fileExistsFn(localBookmarksFileName))
+        if (const auto localBookmarksFileName = AZ::IO::PathView(bookmarkComponent->GetLocalBookmarksFileName()); !m_fileExistsFn(localBookmarksFileName))
         {
             auto registry = AZ::SettingsRegistry::Get();
             if (!registry)

+ 3 - 3
Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/LocalViewBookmarkLoader.h

@@ -52,11 +52,11 @@ namespace AzToolsFramework
     private:
         LocalViewBookmarkComponent* FindOrCreateLocalViewBookmarkComponent();
 
-        void WriteViewBookmarksSettingsRegistryToFile(const AZStd::string& localBookmarksFileName);
-        bool ReadViewBookmarksSettingsRegistryFromFile(const AZStd::string& localBookmarksFileName);
+        void WriteViewBookmarksSettingsRegistryToFile(const AZ::IO::PathView& localBookmarksFileName);
+        bool ReadViewBookmarksSettingsRegistryFromFile(const AZ::IO::PathView& localBookmarksFileName);
 
         //! Writes the content of the string buffer to the stream.
-        bool Write(AZ::IO::GenericStream& genericStream, const AZStd::string& stringBuffer);
+        bool Write(AZ::IO::GenericStream& genericStream, AZStd::string_view stringBuffer);
 
         AZStd::vector<ViewBookmark> m_localBookmarks;
         AZStd::optional<ViewBookmark> m_lastKnownLocation;

+ 4 - 4
Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewBookmarkLoaderInterface.h

@@ -88,14 +88,14 @@ namespace AzToolsFramework
         //! Writable stream interface
         //! Accepts a filename and contents buffer, it will pass the buffer to the third parameter and output to the stream provided.
         using StreamWriteFn = AZStd::function<bool(
-            const AZStd::string& localBookmarksFileName,
-            const AZStd::string& stringBuffer,
+            const AZ::IO::PathView& localBookmarksFileName,
+            AZStd::string_view stringBuffer,
             AZStd::function<bool(AZ::IO::GenericStream& genericStream, const AZStd::string& stringBuffer)>)>;
         //! Readable interface
         //! Will load the file name provided (using project for full path) and return the contents of the file.
-        using StreamReadFn = AZStd::function<AZStd::vector<char>(const AZStd::string& localBookmarksFileName)>;
+        using StreamReadFn = AZStd::function<AZStd::vector<char>(const AZ::IO::PathView& localBookmarksFileName)>;
         // Interface to determine if a file (using project for full path) with the name provided exists already.
-        using FileExistsFn = AZStd::function<bool(const AZStd::string& localBookmarksFileName)>;
+        using FileExistsFn = AZStd::function<bool(const AZ::IO::PathView& localBookmarksFileName)>;
 
         //! Overrides the behavior of writing to a stream.
         //! @note By default this will write to a file on disk.

+ 3 - 3
Code/Framework/AzToolsFramework/Tests/Viewport/ViewBookmarkTests.cpp

@@ -49,7 +49,7 @@ namespace UnitTest
             auto persistentSetReg = AZStd::make_shared<LocalPersistentSettingsRegistry>();
             bookmarkPersistInterface->OverrideStreamWriteFn(
                 [persistentSetReg](
-                    [[maybe_unused]] const AZStd::string& localBookmarksFileName, const AZStd::string& stringBuffer,
+                    const AZ::IO::PathView&, AZStd::string_view stringBuffer,
                     AZStd::function<bool(AZ::IO::GenericStream&, const AZStd::string&)> write)
                 {
                     persistentSetReg->m_buffer.resize(stringBuffer.size() + 1);
@@ -63,13 +63,13 @@ namespace UnitTest
                 });
 
             bookmarkPersistInterface->OverrideStreamReadFn(
-                [persistentSetReg]([[maybe_unused]] const AZStd::string& localBookmarksFileName)
+                [persistentSetReg](const AZ::IO::PathView&)
                 {
                     return persistentSetReg->m_buffer;
                 });
 
             bookmarkPersistInterface->OverrideFileExistsFn(
-                [exists = false](const AZStd::string&) mutable
+                [exists = false](const AZ::IO::PathView&) mutable
                 {
                     // initially does not exist and is then created
                     return AZStd::exchange(exists, true);

+ 25 - 145
Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp

@@ -19,130 +19,6 @@
 
 namespace AssetProcessor
 {
-    void SettingsRegistryBuilder::SettingsExporter::WriteName(AZStd::string_view name)
-    {
-        if (m_includeName)
-        {
-            m_writer.Key(name.data(), aznumeric_caster(name.length()));
-        }
-    }
-
-    SettingsRegistryBuilder::SettingsExporter::SettingsExporter(
-        rapidjson::StringBuffer& buffer, const AZStd::vector<AZStd::string>& excludes)
-        : m_writer(rapidjson::Writer<rapidjson::StringBuffer>(buffer))
-        , m_excludes(excludes)
-    {
-    }
-
-    AZ::SettingsRegistryInterface::VisitResponse SettingsRegistryBuilder::SettingsExporter::Traverse(
-        AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action,
-        AZ::SettingsRegistryInterface::Type type)
-    {
-        for (const AZStd::string& exclude : m_excludes)
-        {
-            if (exclude == path)
-            {
-                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
-            }
-        }
-
-        if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
-        {
-            AZ_Assert(type == AZ::SettingsRegistryInterface::Type::Object || type == AZ::SettingsRegistryInterface::Type::Array,
-                "Unexpected type visited: %i.", type);
-            WriteName(valueName);
-            if (type == AZ::SettingsRegistryInterface::Type::Object)
-            {
-                m_result = m_result && m_writer.StartObject();
-                m_includeNameStack.push(true);
-                m_includeName = true;
-            }
-            else
-            {
-                m_result = m_result && m_writer.StartArray();
-                m_includeNameStack.push(false);
-                m_includeName = false;
-            }
-        }
-        else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
-        {
-            if (type == AZ::SettingsRegistryInterface::Type::Object)
-            {
-                m_result = m_result && m_writer.EndObject();
-            }
-            else
-            {
-                m_result = m_result && m_writer.EndArray();
-            }
-            AZ_Assert(!m_includeNameStack.empty(), "Attempting to close a json array or object that wasn't started.");
-            m_includeNameStack.pop();
-            m_includeName = !m_includeNameStack.empty() ? m_includeNameStack.top() : true;
-        }
-        else if (type == AZ::SettingsRegistryInterface::Type::Null)
-        {
-            WriteName(valueName);
-            m_result = m_result && m_writer.Null();
-        }
-
-        return m_result ?
-            AZ::SettingsRegistryInterface::VisitResponse::Continue :
-            AZ::SettingsRegistryInterface::VisitResponse::Done;
-    }
-
-    void SettingsRegistryBuilder::SettingsExporter::Visit(
-        AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value)
-    {
-        WriteName(valueName);
-        m_result = m_result && m_writer.Bool(value);
-    }
-
-    void SettingsRegistryBuilder::SettingsExporter::Visit(
-        AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
-    {
-        WriteName(valueName);
-        m_result = m_result && m_writer.Int64(value);
-    }
-
-    void SettingsRegistryBuilder::SettingsExporter::Visit(
-        AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::u64 value)
-    {
-        WriteName(valueName);
-        m_result = m_result && m_writer.Uint64(value);
-    }
-
-    void SettingsRegistryBuilder::SettingsExporter::Visit(
-        AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, double value)
-    {
-        WriteName(valueName);
-        m_result = m_result && m_writer.Double(value);
-    }
-
-    void SettingsRegistryBuilder::SettingsExporter::Visit(
-        AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
-    {
-        WriteName(valueName);
-        m_result = m_result && m_writer.String(value.data(), aznumeric_caster(value.length()));
-    }
-
-    bool SettingsRegistryBuilder::SettingsExporter::Finalize()
-    {
-        if (!m_includeNameStack.empty())
-        {
-            AZ_Assert(false, "m_includeNameStack is expected to be empty. This means that there was an object or array what wasn't closed.");
-            return false;
-        }
-        return m_result;
-    }
-
-    void SettingsRegistryBuilder::SettingsExporter::Reset(rapidjson::StringBuffer& buffer)
-    {
-        m_writer.Reset(buffer);
-        m_includeName = false;
-        m_result = true;
-    }
-
-
-
     SettingsRegistryBuilder::SettingsRegistryBuilder()
         : m_builderId("{1BB18B28-2953-4922-A80B-E7375FCD7FC1}")
         , m_assetType("{FEBB3C7B-9C8B-46C3-8AAF-3D132D811087}")
@@ -291,16 +167,25 @@ namespace AssetProcessor
             }
         }
 
-        AZStd::string outputPath;
-        AzFramework::StringFunc::Path::Join(request.m_tempDirPath.c_str(), "bootstrap.game.", outputPath);
-        size_t extensionOffset = outputPath.length();
-
-        rapidjson::StringBuffer outputBuffer;
-        outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
-        SettingsExporter exporter(outputBuffer, excludes);
+        AZ::IO::Path outputPath = AZ::IO::Path(request.m_tempDirPath) / "bootstrap.game";
+        size_t extensionOffset = outputPath.Native().size();
 
         if (!platformCodes.empty())
         {
+            // Setting up the Dumper settings for exporting the settings registry to a file
+            AZStd::string outputBuffer;
+            outputBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
+            AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
+            dumperSettings.m_includeFilter = [&excludes](AZStd::string_view jsonKeyPath)
+            {
+                auto ExcludeField = [&jsonKeyPath](AZStd::string_view excludePath)
+                {
+                    return AZ::SettingsRegistryMergeUtils::IsPathDescendantOrEqual(excludePath, jsonKeyPath);
+                };
+                // Include a path only if it is not equal or a suffix of any paths of the exclude vector
+                return AZStd::ranges::find_if(excludes, ExcludeField) == AZStd::ranges::end(excludes);
+            };
+
             AZStd::string_view platform = platformCodes.front();
             for (size_t i = 0; i < AZStd::size(specializations); ++i)
             {
@@ -391,17 +276,12 @@ namespace AssetProcessor
                     AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, *commandLine, executeRegDumpCommands);
                 }
 
-
-                if (registry.Visit(exporter, ""))
+                if (AZ::IO::ByteContainerStream outputStream(&outputBuffer);
+                    AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(registry, "", outputStream, dumperSettings))
                 {
-                    if (!exporter.Finalize())
-                    {
-                        return;
-                    }
-
                     AZStd::string_view specializationString(specialization.GetSpecialization(0));
-                    outputPath += specializationString; // Append configuration
-                    outputPath += ".setreg";
+                    outputPath.Native() += specializationString; // Append configuration
+                    outputPath.Native() += ".setreg";
 
                     AZ::IO::SystemFile file;
                     if (!file.Open(outputPath.c_str(),
@@ -410,7 +290,7 @@ namespace AssetProcessor
                         AZ_Error("Settings Registry Builder", false, R"(Failed to open file "%s" for writing.)", outputPath.c_str());
                         return;
                     }
-                    if (file.Write(outputBuffer.GetString(), outputBuffer.GetSize()) != outputBuffer.GetSize())
+                    if (file.Write(outputBuffer.data(), outputBuffer.size()) != outputBuffer.size())
                     {
                         AZ_Error("Settings Registry Builder", false, R"(Failed to write settings registry to file "%s".)", outputPath.c_str());
                         return;
@@ -421,14 +301,14 @@ namespace AssetProcessor
                     AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s."
                         " This can result in a product ID collision with other builders for this asset.",
                         AZ_STRING_ARG(specializationString));
-                    response.m_outputProducts.emplace_back(outputPath, m_assetType, hashedSpecialization);
+                    response.m_outputProducts.emplace_back(outputPath.Native(), m_assetType, hashedSpecialization);
                     response.m_outputProducts.back().m_dependenciesHandled = true;
 
-                    outputPath.erase(extensionOffset);
+                    outputPath.Native().erase(extensionOffset);
                 }
 
-                outputBuffer.Clear();
-                exporter.Reset(outputBuffer);
+                // Clear the output buffer, to make sure previous loop iterations settings are not being appended
+                outputBuffer.clear();
             }
         }
 

+ 0 - 28
Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.h

@@ -20,34 +20,6 @@ namespace AssetProcessor
         : public AssetBuilderSDK::AssetBuilderCommandBus::Handler
     {
     public:
-        class SettingsExporter : public AZ::SettingsRegistryInterface::Visitor
-        {
-        public:
-            SettingsExporter(rapidjson::StringBuffer& buffer, const AZStd::vector<AZStd::string>& excludes);
-            ~SettingsExporter() override = default;
-
-            AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
-                AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type) override;
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override;
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::u64 value) override;
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, double value) override;
-            void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-            bool Finalize();
-            void Reset(rapidjson::StringBuffer& buffer);
-            
-        private:
-            rapidjson::Writer<rapidjson::StringBuffer> m_writer;
-
-            const AZStd::vector<AZStd::string>& m_excludes;
-            AZStd::stack<bool> m_includeNameStack;
-            bool m_includeName{ false };
-            bool m_result{ true };
-
-            void WriteName(AZStd::string_view name);
-        };
-
         SettingsRegistryBuilder();
         ~SettingsRegistryBuilder() override = default;
 

+ 63 - 26
Code/Tools/AssetProcessor/native/tests/InternalBuilders/SettingsRegistryBuilderTests.cpp

@@ -46,14 +46,23 @@ namespace AssetProcessor
 
         AZ::SettingsRegistryImpl registry;
         ASSERT_TRUE(registry.MergeSettings(json, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
-        rapidjson::StringBuffer registryOutputBuffer;
+        AZStd::string registryOutputBuffer;
         AZStd::vector<AZStd::string> excludes;
-        SettingsRegistryBuilder::SettingsExporter exporter(registryOutputBuffer, excludes);
-        registry.Visit(exporter, "/TestValues");
-        ASSERT_TRUE(exporter.Finalize());
-
-        EXPECT_EQ(jsonOutputBuffer.GetLength(), registryOutputBuffer.GetLength());
-        EXPECT_STREQ(jsonOutputBuffer.GetString(), registryOutputBuffer.GetString());
+        AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
+        dumperSettings.m_includeFilter = [&excludes](AZStd::string_view jsonKeyPath)
+        {
+            auto ExcludeField = [&jsonKeyPath](AZStd::string_view excludePath)
+            {
+                return AZ::SettingsRegistryMergeUtils::IsPathDescendantOrEqual(excludePath, jsonKeyPath);
+            };
+            // Include a path only if it is not equal or a suffix of any paths of the exclude vector
+            return AZStd::ranges::find_if(excludes, ExcludeField) == AZStd::ranges::end(excludes);
+        };
+        AZ::IO::ByteContainerStream byteStream(&registryOutputBuffer);
+        ASSERT_TRUE(AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(registry, "/TestValues", byteStream, dumperSettings));
+
+        EXPECT_EQ(jsonOutputBuffer.GetLength(), registryOutputBuffer.size());
+        EXPECT_STREQ(jsonOutputBuffer.GetString(), registryOutputBuffer.c_str());
     }
 
     TEST_F(SettingsRegistryBuilderTest, SettingsExporter_FilterOutSection_FieldNotInOutput)
@@ -75,15 +84,24 @@ namespace AssetProcessor
 
         AZ::SettingsRegistryImpl registry;
         ASSERT_TRUE(registry.MergeSettings(json, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
-        rapidjson::StringBuffer registryOutputBuffer;
+        AZStd::string registryOutputBuffer;
         AZStd::vector<AZStd::string> excludes;
         excludes.push_back("/TestValues/A/B");
-        SettingsRegistryBuilder::SettingsExporter exporter(registryOutputBuffer, excludes);
-        registry.Visit(exporter, "/TestValues");
-        ASSERT_TRUE(exporter.Finalize());
+        AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
+        dumperSettings.m_includeFilter = [&excludes](AZStd::string_view jsonKeyPath)
+        {
+            auto ExcludeField = [&jsonKeyPath](AZStd::string_view excludePath)
+            {
+                return AZ::SettingsRegistryMergeUtils::IsPathDescendantOrEqual(excludePath, jsonKeyPath);
+            };
+            // Include a path only if it is not equal or a suffix of any paths of the exclude vector
+            return AZStd::ranges::find_if(excludes, ExcludeField) == AZStd::ranges::end(excludes);
+        };
+        AZ::IO::ByteContainerStream byteStream(&registryOutputBuffer);
+        ASSERT_TRUE(AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(registry, "/TestValues", byteStream, dumperSettings));
 
         rapidjson::Document document;
-        document.Parse(registryOutputBuffer.GetString(), registryOutputBuffer.GetLength());
+        document.Parse(registryOutputBuffer.c_str(), registryOutputBuffer.size());
         ASSERT_FALSE(document.HasParseError());
 
         auto it = document.FindMember("A");
@@ -101,14 +119,23 @@ namespace AssetProcessor
 
         AZ::SettingsRegistryImpl registry;
         ASSERT_TRUE(registry.MergeSettings(json, AZ::SettingsRegistryInterface::Format::JsonPatch));
-        rapidjson::StringBuffer registryOutputBuffer;
+        AZStd::string registryOutputBuffer;
         AZStd::vector<AZStd::string> excludes;
-        SettingsRegistryBuilder::SettingsExporter exporter(registryOutputBuffer, excludes);
-        registry.Visit(exporter, "/TestValues");
-        ASSERT_TRUE(exporter.Finalize());
+        AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
+        dumperSettings.m_includeFilter = [&excludes](AZStd::string_view jsonKeyPath)
+        {
+            auto ExcludeField = [&jsonKeyPath](AZStd::string_view excludePath)
+            {
+                return AZ::SettingsRegistryMergeUtils::IsPathDescendantOrEqual(excludePath, jsonKeyPath);
+            };
+            // Include a path only if it is not equal or a suffix of any paths of the exclude vector
+            return AZStd::ranges::find_if(excludes, ExcludeField) == AZStd::ranges::end(excludes);
+        };
+        AZ::IO::ByteContainerStream byteStream(&registryOutputBuffer);
+        ASSERT_TRUE(AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(registry, "/TestValues", byteStream, dumperSettings));
 
         rapidjson::Document document;
-        document.Parse(registryOutputBuffer.GetString(), registryOutputBuffer.GetLength());
+        document.Parse(registryOutputBuffer.c_str(), registryOutputBuffer.size());
         ASSERT_FALSE(document.HasParseError());
 
         auto it = document.FindMember("Null");
@@ -132,14 +159,24 @@ namespace AssetProcessor
         AZ::SettingsRegistryImpl registrySecond;
         ASSERT_TRUE(registrySecond.MergeSettings(jsonSecond, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
 
-        rapidjson::StringBuffer registryOutputBuffer;
+        AZStd::string registryOutputBuffer;
         AZStd::vector<AZStd::string> excludes;
-        SettingsRegistryBuilder::SettingsExporter exporter(registryOutputBuffer, excludes);
-        registryFirst.Visit(exporter, "/TestValues");
-        ASSERT_TRUE(exporter.Finalize());
-        registryOutputBuffer.Clear();
-        exporter.Reset(registryOutputBuffer);
-        registrySecond.Visit(exporter, "/TestValues");
+        AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
+        dumperSettings.m_includeFilter = [&excludes](AZStd::string_view jsonKeyPath)
+        {
+            auto ExcludeField = [&jsonKeyPath](AZStd::string_view excludePath)
+            {
+                return AZ::SettingsRegistryMergeUtils::IsPathDescendantOrEqual(excludePath, jsonKeyPath);
+            };
+            // Include a path only if it is not equal or a suffix of any paths of the exclude vector
+            return AZStd::ranges::find_if(excludes, ExcludeField) == AZStd::ranges::end(excludes);
+        };
+        AZ::IO::ByteContainerStream byteStream(&registryOutputBuffer);
+        ASSERT_TRUE(AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(registryFirst, "/TestValues", byteStream, dumperSettings));
+
+        registryOutputBuffer.clear();
+        byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
+        ASSERT_TRUE(AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(registrySecond, "/TestValues", byteStream, dumperSettings));
 
         rapidjson::Document document;
         document.Parse(jsonSecond);
@@ -148,7 +185,7 @@ namespace AssetProcessor
         rapidjson::Writer<rapidjson::StringBuffer> writer(jsonOutputBuffer);
         document.FindMember("TestValues")->value.Accept(writer);
 
-        EXPECT_EQ(jsonOutputBuffer.GetLength(), registryOutputBuffer.GetLength());
-        EXPECT_STREQ(jsonOutputBuffer.GetString(), registryOutputBuffer.GetString());
+        EXPECT_EQ(jsonOutputBuffer.GetLength(), registryOutputBuffer.size());
+        EXPECT_STREQ(jsonOutputBuffer.GetString(), registryOutputBuffer.c_str());
     }
 } // namespace AssetProcessor

+ 225 - 451
Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp

@@ -29,78 +29,119 @@ namespace
 
 namespace AssetProcessor
 {
-
-    void AssetImporterPathsVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type,
-        AZStd::string_view value)
+    struct AssetImporterPathsVisitor
+        : AZ::SettingsRegistryInterface::Visitor
     {
-        auto found = value.find('.');
-        if (found != AZStd::string::npos)
+        AssetImporterPathsVisitor(AZ::SettingsRegistryInterface* settingsRegistry, AZStd::vector<AZStd::string>& supportedExtension)
+            : m_settingsRegistry(settingsRegistry)
+            , m_supportedFileExtensions(supportedExtension)
         {
-            m_supportedFileExtensions.emplace_back(value.substr(found + 1));
         }
-        else
-        {
-            m_supportedFileExtensions.emplace_back(value);
-        }
-    }
 
-    struct PlatformsInfoVisitor
-        : AZ::SettingsRegistryInterface::Visitor
-    {
-        AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-            AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+        void Visit(const AZ::SettingsRegistryInterface::VisitArgs&, AZStd::string_view value) override
         {
-            constexpr AZStd::string_view PlatformInfoPrefix = "Platform ";
-            switch (action)
+            if (auto found = value.find('.'); found != AZStd::string::npos)
             {
-            case AZ::SettingsRegistryInterface::VisitAction::Begin:
-            {
-                // Only continue traversal if the path is exactly the AssetProcessorSettingsKey (which indicates the start of traversal)
-                // or if a "Platform *" object and it's children are being traversed
-                if (jsonPath == AssetProcessorSettingsKey)
-                {
-                    return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-                }
-                if (valueName.starts_with(PlatformInfoPrefix))
-                {
-                    // Retrieve the platform name from the rest of valueName portion of the key "Platform (.*)"
-                    AZStd::string platformIdentifier = valueName.substr(PlatformInfoPrefix.size());
-                    // Lowercase the platformIdentifier and store it in the stack
-                    AZStd::to_lower(platformIdentifier.begin(), platformIdentifier.end());
-
-                    m_platformIdentifierStack.push(AZStd::move(platformIdentifier));
-                }
+                m_supportedFileExtensions.emplace_back(value.substr(found + 1));
             }
-            break;
-            case AZ::SettingsRegistryInterface::VisitAction::End:
+            else
             {
-                if (valueName.starts_with(PlatformInfoPrefix))
-                {
-                    AZ_Assert(!m_platformIdentifierStack.empty(), "PlatformInfo stack should not be empty. More stack pops, than pushes");
-                    m_platformIdentifierStack.pop();
-                }
+                m_supportedFileExtensions.emplace_back(value);
             }
-            break;
+        }
 
-            default:
-                break;
-            }
+        AZ::SettingsRegistryInterface* m_settingsRegistry;
+        AZStd::vector<AZStd::string> m_supportedFileExtensions;
+    };
+
+    //! Visitor for reading the "/Amazon/AssetProcessor/Settings/ScanFolder *" entries from the Settings Registry
+    //! Expects the key to path to the visitor to be "/Amazon/AssetProcessor/Settings"
+    struct ScanFolderVisitor
+        : AZ::SettingsRegistryVisitorUtils::ObjectVisitor
+    {
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+        AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) override;
+
+        struct ScanFolderInfo
+        {
+            AZStd::string m_scanFolderIdentifier;
+            AZStd::string m_scanFolderDisplayName;
+            AZ::IO::Path m_watchPath{ AZ::IO::PosixPathSeparator };
+            AZStd::vector<AZStd::string> m_includeIdentifiers;
+            AZStd::vector<AZStd::string> m_excludeIdentifiers;
+            int m_scanOrder{};
+            bool m_isRecursive{};
+        };
+        AZStd::vector<ScanFolderInfo> m_scanFolderInfos;
+    };
+
+    struct ExcludeVisitor
+        : AZ::SettingsRegistryVisitorUtils::ObjectVisitor
+    {
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+        AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) override;
+
+        AZStd::vector<ExcludeAssetRecognizer> m_excludeAssetRecognizers;
+    };
 
-            return !m_platformIdentifierStack.empty() ? AZ::SettingsRegistryInterface::VisitResponse::Continue
-                : AZ::SettingsRegistryInterface::VisitResponse::Skip;
+    struct SimpleJobVisitor
+        : AZ::SettingsRegistryVisitorUtils::ObjectVisitor
+    {
+        SimpleJobVisitor(const AZStd::vector<AssetBuilderSDK::PlatformInfo>& enabledPlatforms)
+            : m_enabledPlatforms(enabledPlatforms)
+        {
         }
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+        AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) override;
+
+        struct SimpleJobAssetRecognizer
+        {
+            AssetRecognizer m_recognizer;
+            AZStd::string m_defaultParams;
+            bool m_ignore{};
+        };
+        AZStd::vector<SimpleJobAssetRecognizer> m_assetRecognizers;
+    private:
+        void ApplyParamsOverrides(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
+            SimpleJobAssetRecognizer& assetRecognizer);
 
+        const AZStd::vector<AssetBuilderSDK::PlatformInfo>& m_enabledPlatforms;
+    };
+
+    //! This vistor reads in the Asset Cache Server configuration elements from the settings registry
+    struct ACSVisitor
+        : AZ::SettingsRegistryVisitorUtils::ObjectVisitor
+    {
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+        AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) override;
+
+        AZStd::vector<AssetRecognizer> m_assetRecognizers;
+    };
+
+    struct PlatformsInfoVisitor
+        : AZ::SettingsRegistryVisitorUtils::ObjectVisitor
+    {
         using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
+        AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) override
         {
-            if (m_platformIdentifierStack.empty())
+            // Visit any each "Platform *" field that is a direct child of the object at the AssetProcessorSettingsKey
+            constexpr AZStd::string_view PlatformInfoPrefix = "Platform ";
+            if (!visitArgs.m_fieldName.starts_with(PlatformInfoPrefix))
             {
-                return;
+                return AZ::SettingsRegistryInterface::VisitResponse::Skip;
             }
 
-            if (valueName == "tags")
+            // Retrieve the platform name from the rest of valueName portion of the key "Platform (.*)"
+            AZStd::string platformIdentifier = visitArgs.m_fieldName.substr(PlatformInfoPrefix.size());
+            // Lowercase the platformIdentifier
+            AZStd::to_lower(platformIdentifier.begin(), platformIdentifier.end());
+
+            // Look up the "tags" field that is child of the "Platform (.*)" field
+            using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+            const auto tagKeyPath = FixedValueString(visitArgs.m_jsonKeyPath) + "/tags";
+            if (AZStd::string tagValue; visitArgs.m_registry.Get(tagValue, tagKeyPath))
             {
-                AZStd::string_view platformIdentifier = m_platformIdentifierStack.top();
                 AZStd::unordered_set<AZStd::string> platformTags;
                 auto JoinTags = [&platformTags](AZStd::string_view token)
                 {
@@ -108,23 +149,23 @@ namespace AssetProcessor
                     AZStd::to_lower(cleanedTag.begin(), cleanedTag.end());
                     platformTags.insert(AZStd::move(cleanedTag));
                 };
-                AZ::StringFunc::TokenizeVisitor(value, JoinTags, ',');
+                AZ::StringFunc::TokenizeVisitor(tagValue, JoinTags, ',');
                 m_platformInfos.emplace_back(platformIdentifier, platformTags);
             }
+
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         }
 
         AZStd::vector<AssetBuilderSDK::PlatformInfo> m_platformInfos;
-    private:
-        AZStd::stack<AZStd::string> m_platformIdentifierStack;
     };
 
     struct MetaDataTypesVisitor
         : AZ::SettingsRegistryInterface::Visitor
     {
         using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
+        void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZStd::string_view value) override
         {
-            m_metaDataTypes.push_back({ AZ::IO::PathView(valueName, AZ::IO::PosixPathSeparator).LexicallyNormal().String(), value });
+            m_metaDataTypes.push_back({ AZ::IO::PathView(visitArgs.m_fieldName, AZ::IO::PosixPathSeparator).LexicallyNormal().String(), value });
         }
 
         struct MetaDataType
@@ -135,193 +176,79 @@ namespace AssetProcessor
         AZStd::vector<MetaDataType> m_metaDataTypes;
     };
 
-    AZ::SettingsRegistryInterface::VisitResponse ScanFolderVisitor::Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-        AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type)
-    {
-        constexpr AZStd::string_view ScanFolderInfoPrefix = "ScanFolder ";
-        switch (action)
-        {
-        case AZ::SettingsRegistryInterface::VisitAction::Begin:
-        {
-            if (jsonPath == AssetProcessorSettingsKey)
-            {
-                return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-            }
-            if (valueName.starts_with(ScanFolderInfoPrefix))
-            {
-                // Retrieve the ScanFolder identifier from "Scan Folder *"
-                AZStd::string scanFolderDisplayName = valueName.substr(ScanFolderInfoPrefix.size());
-                m_scanFolderStack.push(scanFolderDisplayName);
-
-                ScanFolderInfo& scanFolderInfo = m_scanFolderInfos.emplace_back();
-                scanFolderInfo.m_scanFolderIdentifier = scanFolderDisplayName;
-                scanFolderInfo.m_scanFolderDisplayName = AZStd::move(scanFolderDisplayName);
-            }
-        }
-        break;
-        case AZ::SettingsRegistryInterface::VisitAction::End:
-        {
-            if (valueName.starts_with(ScanFolderInfoPrefix))
-            {
-                AZ_Assert(!m_scanFolderStack.empty(), "ScanFolder identifier stack should not be empty. More stack pops, than pushes");
-                m_scanFolderStack.pop();
-            }
-        }
-        break;
 
-        default:
-            break;
-        }
-
-        return !m_scanFolderStack.empty() ? AZ::SettingsRegistryInterface::VisitResponse::Continue
-            : AZ::SettingsRegistryInterface::VisitResponse::Skip;
-    }
-    void ScanFolderVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
+    AZ::SettingsRegistryInterface::VisitResponse ScanFolderVisitor::Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
     {
+        constexpr AZStd::string_view ScanFolderInfoPrefix = "ScanFolder ";
         // Check if a "ScanFolder *" element is being traversed
-        if (m_scanFolderStack.empty())
+        if (!visitArgs.m_fieldName.starts_with(ScanFolderInfoPrefix))
         {
-            return;
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         }
 
-        AZStd::string_view currentScanFolderIdentifier = m_scanFolderStack.top();
-
-        // Find ScanFolder element being iterated over.
-        // It should be the last element in ScanFolderInfo vector
-        auto scanFolderEntryIt = AZStd::find_if(m_scanFolderInfos.rbegin(), m_scanFolderInfos.rend(),
-            [&currentScanFolderIdentifier](const ScanFolderInfo& scanFolderInfo)
-        {
-            return scanFolderInfo.m_scanFolderIdentifier == currentScanFolderIdentifier;
-        });
-        if (scanFolderEntryIt == m_scanFolderInfos.rend())
-        {
-            return;
-        }
+        AZStd::string_view currentScanFolderIdentifier = visitArgs.m_fieldName.substr(ScanFolderInfoPrefix.size());
 
-        ScanFolderInfo& scanFolderEntry = *scanFolderEntryIt;
-        if (valueName == "recursive")
-        {
-            scanFolderEntry.m_isRecursive = value != 0;
-        }
-        else if (valueName == "order")
-        {
-            scanFolderEntry.m_scanOrder = static_cast<int>(value);
-        }
-    }
+        ScanFolderInfo& scanFolderInfo = m_scanFolderInfos.emplace_back();
+        scanFolderInfo.m_scanFolderIdentifier = currentScanFolderIdentifier;
+        scanFolderInfo.m_scanFolderDisplayName = currentScanFolderIdentifier;
 
-    void ScanFolderVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
-    {
-        // Check if a "ScanFolder *" element is being traversed
-        if (m_scanFolderStack.empty())
+        using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+        if (AZ::s64 value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/recursive"))
         {
-            return;
+            scanFolderInfo.m_isRecursive = value != 0;
         }
-
-        AZStd::string_view currentScanFolderIdentifier = m_scanFolderStack.top();
-
-        // Find ScanFolder element being iterated over.
-        // It should be the last element in ScanFolderInfo vector
-        auto scanFolderEntryIt = AZStd::find_if(m_scanFolderInfos.rbegin(), m_scanFolderInfos.rend(),
-            [&currentScanFolderIdentifier](const ScanFolderInfo& scanFolderInfo)
+        if (AZ::s64 value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/order"))
         {
-            return scanFolderInfo.m_scanFolderIdentifier == currentScanFolderIdentifier;
-        });
-        if (scanFolderEntryIt == m_scanFolderInfos.rend())
-        {
-            return;
+            scanFolderInfo.m_scanOrder = static_cast<int>(value);
         }
 
-        ScanFolderInfo& scanFolderEntry = *scanFolderEntryIt;
-        if (valueName == "watch")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/watch"))
         {
-            scanFolderEntry.m_watchPath = value;
+            scanFolderInfo.m_watchPath = value;
         }
-        else if (valueName == "display" && !value.empty())
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/display")
+            && !value.empty())
         {
-            scanFolderEntry.m_scanFolderDisplayName = value;
+            scanFolderInfo.m_scanFolderDisplayName = value;
         }
-        else if (valueName == "include")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/include"))
         {
-            auto JoinTags = [&scanFolderEntry](AZStd::string_view token)
+            auto JoinTags = [&scanFolderInfo](AZStd::string_view token)
             {
-                scanFolderEntry.m_includeIdentifiers.push_back(token);
+                scanFolderInfo.m_includeIdentifiers.push_back(token);
             };
             AZ::StringFunc::TokenizeVisitor(value, JoinTags, ',');
         }
-        else if (valueName == "exclude")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/exclude"))
         {
-            auto JoinTags = [&scanFolderEntry](AZStd::string_view token)
+            auto JoinTags = [&scanFolderInfo](AZStd::string_view token)
             {
-                scanFolderEntry.m_excludeIdentifiers.push_back(token);
+                scanFolderInfo.m_excludeIdentifiers.push_back(token);
             };
             AZ::StringFunc::TokenizeVisitor(value, JoinTags, ',');
         }
-    }
-
-    AZ::SettingsRegistryInterface::VisitResponse ExcludeVisitor::Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-        AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type)
-    {
-        constexpr AZStd::string_view ExcludeNamePrefix = "Exclude ";
-        switch (action)
-        {
-        case AZ::SettingsRegistryInterface::VisitAction::Begin:
-        {
-            if (jsonPath == AssetProcessorSettingsKey)
-            {
-                return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-            }
-            if (valueName.starts_with(ExcludeNamePrefix))
-            {
-                // Extract the substr that is part of the valueName "Exclude *"
-                AZStd::string excludeName = valueName.substr(ExcludeNamePrefix.size());
-                m_excludeNameStack.push(excludeName);
-
-                ExcludeAssetRecognizer& excludeAssetRecognizer = m_excludeAssetRecognizers.emplace_back();
-                excludeAssetRecognizer.m_name = QString::fromUtf8(excludeName.c_str(), aznumeric_cast<int>(excludeName.size()));
-            }
-        }
-        break;
-        case AZ::SettingsRegistryInterface::VisitAction::End:
-        {
-            if (valueName.starts_with(ExcludeNamePrefix))
-            {
-                AZ_Assert(!m_excludeNameStack.empty(), "Exclude stack should not be empty. More stack pops, than pushes");
-                m_excludeNameStack.pop();
-            }
-        }
-        break;
-
-        default:
-            break;
-        }
 
-        return !m_excludeNameStack.empty() ? AZ::SettingsRegistryInterface::VisitResponse::Continue
-            : AZ::SettingsRegistryInterface::VisitResponse::Skip;
+        return AZ::SettingsRegistryInterface::VisitResponse::Skip;
     }
 
-    void ExcludeVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
+    AZ::SettingsRegistryInterface::VisitResponse ExcludeVisitor::Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
     {
-        if (m_excludeNameStack.empty())
+        constexpr AZStd::string_view ExcludeNamePrefix = "Exclude ";
+        if (!visitArgs.m_fieldName.starts_with(ExcludeNamePrefix))
         {
-            return;
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         }
 
-        AZStd::string_view excludeNameView = m_excludeNameStack.top();
-        auto excludeName = QString::fromUtf8(excludeNameView.data(), aznumeric_cast<int>(excludeNameView.size()));
-
-        // Find ScanFolder element being iterated over.
-        // It should be the last element in ScanFolderInfo vector
-        auto excludeAssetRecognizerEntryIt = AZStd::find_if(m_excludeAssetRecognizers.rbegin(), m_excludeAssetRecognizers.rend(),
-            [&excludeName](const ExcludeAssetRecognizer& excludeAssetRecognizer)
-        {
-            return excludeAssetRecognizer.m_name == excludeName;
-        });
-        if (excludeAssetRecognizerEntryIt == m_excludeAssetRecognizers.rend())
-        {
-            return;
-        }
+        AZStd::string_view excludeName = visitArgs.m_fieldName.substr(ExcludeNamePrefix.size());
+        ExcludeAssetRecognizer& excludeAssetRecognizer = m_excludeAssetRecognizers.emplace_back();
+        excludeAssetRecognizer.m_name = QString::fromUtf8(excludeName.data(), aznumeric_cast<int>(excludeName.size()));
 
-        ExcludeAssetRecognizer& excludeAssetRecognizer = *excludeAssetRecognizerEntryIt;
 
         // The "pattern" and "glob" entries were previously parsed by QSettings which un-escapes the values
         // To compensate for it the AssetProcessorPlatformConfig.ini was escaping the
@@ -351,7 +278,9 @@ namespace AssetProcessor
             return unescapedResult;
         };
 
-        if (valueName == "pattern")
+        using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/pattern"))
         {
             if (!value.empty())
             {
@@ -359,7 +288,8 @@ namespace AssetProcessor
                 excludeAssetRecognizer.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
             }
         }
-        else if (valueName == "glob")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/glob"))
         {
             if (!excludeAssetRecognizer.m_patternMatcher.IsValid())
             {
@@ -367,147 +297,63 @@ namespace AssetProcessor
                 excludeAssetRecognizer.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
             }
         }
+
+        return AZ::SettingsRegistryInterface::VisitResponse::Skip;
     }
 
-    AZ::SettingsRegistryInterface::VisitResponse SimpleJobVisitor::Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-        AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type)
+    AZ::SettingsRegistryInterface::VisitResponse SimpleJobVisitor::Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
     {
         constexpr AZStd::string_view RCNamePrefix = "RC "; // RC = Resource Compiler
         constexpr AZStd::string_view SJNamePrefix = "SJ "; // SJ = Simple Job
-        switch (action)
-        {
-        case AZ::SettingsRegistryInterface::VisitAction::Begin:
-        {
-            if (jsonPath == AssetProcessorSettingsKey)
-            {
-                return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-            }
-            if (valueName.starts_with(RCNamePrefix) || valueName.starts_with(SJNamePrefix))
-            {
-                // Extract the substr that is part of the valueName "Exclude *"
-                AZStd::string rcName = valueName.substr(SJNamePrefix.size());
-                m_simpleJobNameStack.push(rcName);
 
-                auto& assetRecognizer = m_assetRecognizers.emplace_back();
-                assetRecognizer.m_recognizer.m_name = rcName;
-            }
-        }
-        break;
-        case AZ::SettingsRegistryInterface::VisitAction::End:
-        {
-            if (valueName.starts_with(RCNamePrefix) || valueName.starts_with(SJNamePrefix))
-            {
-                AZ_Assert(!m_simpleJobNameStack.empty(), "SimpleJob name stack should not be empty. More stack pops, than pushes");
-                ApplyParamsOverrides(jsonPath);
-                m_simpleJobNameStack.pop();
-            }
-        }
-        break;
-
-        default:
-            break;
-        }
-
-        return !m_simpleJobNameStack.empty() ? AZ::SettingsRegistryInterface::VisitResponse::Continue
-            : AZ::SettingsRegistryInterface::VisitResponse::Skip;
-    }
-
-    void SimpleJobVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value)
-    {
-        if (m_simpleJobNameStack.empty())
+        if (!visitArgs.m_fieldName.starts_with(RCNamePrefix) && !visitArgs.m_fieldName.starts_with(SJNamePrefix))
         {
-            return;
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         }
 
-        AZStd::string_view sjNameView = m_simpleJobNameStack.top();
+        AZStd::string_view sjNameView = visitArgs.m_fieldName.starts_with(SJNamePrefix)
+            ? visitArgs.m_fieldName.substr(SJNamePrefix.size())
+            : visitArgs.m_fieldName.substr(RCNamePrefix.size());
 
-        // Find AssetRecognizer identified by the top entry in the name stack
-        auto assetRecognizerEntryIt = AZStd::find_if(m_assetRecognizers.rbegin(), m_assetRecognizers.rend(),
-            [&sjNameView](const auto& assetRecognizer)
-        {
-            return assetRecognizer.m_recognizer.m_name == sjNameView;
-        });
-        if (assetRecognizerEntryIt == m_assetRecognizers.rend())
-        {
-            return;
-        }
+        auto& assetRecognizer = m_assetRecognizers.emplace_back();
+        assetRecognizer.m_recognizer.m_name = sjNameView;
 
-        auto& assetRecognizer = *assetRecognizerEntryIt;
-        if (valueName == "ignore")
+        using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/ignore"))
         {
             assetRecognizer.m_ignore = value;
         }
-        else if (valueName == "lockSource")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/lockSource"))
         {
             assetRecognizer.m_recognizer.m_testLockSource = value;
         }
-        else if (valueName == "critical")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/critical"))
         {
             assetRecognizer.m_recognizer.m_isCritical = value;
         }
-        else if (valueName == "checkServer")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/checkServer"))
         {
             assetRecognizer.m_recognizer.m_checkServer = value;
         }
-        else if (valueName == "supportsCreateJobs")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/supportsCreateJobs"))
         {
             assetRecognizer.m_recognizer.m_supportsCreateJobs = value;
         }
-        else if (valueName == "outputProductDependencies")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/outputProductDependencies"))
         {
             assetRecognizer.m_recognizer.m_outputProductDependencies = value;
         }
-    }
-
-    void SimpleJobVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
-    {
-        if (m_simpleJobNameStack.empty())
-        {
-            return;
-        }
-
-        AZStd::string_view sjNameView = m_simpleJobNameStack.top();
-
-        // Find AssetRecognizer identified by the top entry in the name stack
-        auto assetRecognizerEntryIt = AZStd::find_if(m_assetRecognizers.rbegin(), m_assetRecognizers.rend(),
-            [&sjNameView](const auto& assetRecognizer)
-            {
-            return assetRecognizer.m_recognizer.m_name == sjNameView;
-            });
-        if (assetRecognizerEntryIt == m_assetRecognizers.rend())
-        {
-            return;
-        }
-
-        auto& assetRecognizer = *assetRecognizerEntryIt;
-        if (valueName == "priority")
+        if (AZ::s64 value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/priority"))
         {
             assetRecognizer.m_recognizer.m_priority = static_cast<int>(value);
         }
-    }
-
-    void SimpleJobVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
-    {
-        if (m_simpleJobNameStack.empty())
-        {
-            return;
-        }
-
-        AZStd::string_view sjNameView = m_simpleJobNameStack.top();
-        
-
-        // Find AssetRecognizer identified by the top entry in the name stack
-        auto assetRecognizerEntryIt = AZStd::find_if(m_assetRecognizers.rbegin(), m_assetRecognizers.rend(),
-            [&sjNameView](const SimpleJobAssetRecognizer& assetRecognizer)
-            {
-                return assetRecognizer.m_recognizer.m_name == sjNameView;
-            });
-        if (assetRecognizerEntryIt == m_assetRecognizers.rend())
-        {
-            return;
-        }
-
-        auto& assetRecognizer = *assetRecognizerEntryIt;
 
         // The "pattern" and "glob" entries were previously parsed by QSettings which un-escapes the values
         // To compensate for it the AssetProcessorPlatformConfig.ini was escaping the
@@ -537,7 +383,8 @@ namespace AssetProcessor
             return unescapedResult;
         };
 
-        if (valueName == "pattern")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/pattern"))
         {
             if (!value.empty())
             {
@@ -545,7 +392,8 @@ namespace AssetProcessor
                 assetRecognizer.m_recognizer.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
             }
         }
-        else if (valueName == "glob")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/glob"))
         {
             // Add the glob pattern if it the matter matcher doesn't already contain a valid regex pattern
             if (!assetRecognizer.m_recognizer.m_patternMatcher.IsValid())
@@ -554,11 +402,13 @@ namespace AssetProcessor
                 assetRecognizer.m_recognizer.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
             }
         }
-        else if (valueName == "version")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/version"))
         {
             assetRecognizer.m_recognizer.m_version = value;
         }
-        else if (valueName == "productAssetType")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/productAssetType"))
         {
             if (!value.empty())
             {
@@ -569,35 +419,18 @@ namespace AssetProcessor
                 }
             }
         }
-        else if (valueName == "params")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/params"))
         {
             assetRecognizer.m_defaultParams = value;
         }
+
+        ApplyParamsOverrides(visitArgs, assetRecognizer);
+        return AZ::SettingsRegistryInterface::VisitResponse::Skip;
     }
 
-    void SimpleJobVisitor::ApplyParamsOverrides(AZStd::string_view path)
+    void SimpleJobVisitor::ApplyParamsOverrides(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, SimpleJobAssetRecognizer& assetRecognizer)
     {
-        if (m_simpleJobNameStack.empty())
-        {
-            return;
-        }
-
-        AZStd::string_view sjNameView = m_simpleJobNameStack.top();
-
-
-        // Find AssetRecognizer identified by the top entry in the name stack
-        auto assetRecognizerEntryIt = AZStd::find_if(m_assetRecognizers.rbegin(), m_assetRecognizers.rend(),
-            [&sjNameView](const SimpleJobAssetRecognizer& assetRecognizer)
-        {
-            return assetRecognizer.m_recognizer.m_name == sjNameView;
-        });
-        if (assetRecognizerEntryIt == m_assetRecognizers.rend())
-        {
-            return;
-        }
-
-        auto& assetRecognizer = *assetRecognizerEntryIt;
-
         /* so in this particular case we want to end up with an AssetPlatformSpec struct that
             has only got the platforms that 'matter' in it
             so for example, if you have the following enabled platforms
@@ -643,12 +476,12 @@ namespace AssetProcessor
             AZStd::string_view currentParams = assetRecognizer.m_defaultParams;
             // The "/Amazon/AssetProcessor/Settings/SJ */<platform>" entry will be queried
             AZ::IO::Path overrideParamsKey = AZ::IO::Path(AZ::IO::PosixPathSeparator);
-            overrideParamsKey /= path;
+            overrideParamsKey /= visitArgs.m_jsonKeyPath;
             overrideParamsKey /= platform.m_identifier;
 
             AZ::SettingsRegistryInterface::FixedValueString overrideParamsValue;
             // Check if the enabled platform identifier matches a key within the "SJ *" object
-            if (m_registry.Get(overrideParamsValue, overrideParamsKey.Native()))
+            if (visitArgs.m_registry.Get(overrideParamsValue, overrideParamsKey.Native()))
             {
                 currentParams = overrideParamsValue;
             }
@@ -658,7 +491,7 @@ namespace AssetProcessor
                 for (const AZStd::string& tag : platform.m_tags)
                 {
                     overrideParamsKey.ReplaceFilename(AZ::IO::PathView(tag));
-                    if (m_registry.Get(overrideParamsValue, overrideParamsKey.Native()))
+                    if (visitArgs.m_registry.Get(overrideParamsValue, overrideParamsKey.Native()))
                     {
                         // if we get here it means we found a tag that applies to this platform
                         currentParams = overrideParamsValue;
@@ -675,115 +508,50 @@ namespace AssetProcessor
         }
     }
 
-    // Find the current AssetRecognizer identified by the top entry in the name stack
-    AssetRecognizer* ACSVisitor::CurrentAssetRecognizer()
-    {
-        if (m_nameStack.empty())
-        {
-            return nullptr;
-        }
-
-        auto& nameView = m_nameStack.top();
-
-        auto assetRecognizerEntryIt = AZStd::find_if(m_assetRecognizers.rbegin(), m_assetRecognizers.rend(),
-            [&nameView](const AssetRecognizer& assetRecognizer)
-            {
-                return assetRecognizer.m_name == nameView;
-            });
-        if (assetRecognizerEntryIt == m_assetRecognizers.rend())
-        {
-            return nullptr;
-        }
-        return &(*assetRecognizerEntryIt);
-    }
-
-    AZ::SettingsRegistryInterface::VisitResponse ACSVisitor::Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-        AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type)
+    AZ::SettingsRegistryInterface::VisitResponse ACSVisitor::Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
     {
         constexpr AZStd::string_view ACSNamePrefix = "ACS ";
-        switch (action)
-        {
-        case AZ::SettingsRegistryInterface::VisitAction::Begin:
+        if (!visitArgs.m_fieldName.starts_with(ACSNamePrefix))
         {
-            if (jsonPath == AssetProcessorServerKey)
-            {
-                return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-            }
-            if (valueName.starts_with(ACSNamePrefix))
-            {
-                AZStd::string name = valueName.substr(ACSNamePrefix.size());
-                m_nameStack.push(name);
-
-                AssetRecognizer& assetRecognizer = m_assetRecognizers.emplace_back();
-                assetRecognizer.m_name = name;
-            }
-        }
-        break;
-        case AZ::SettingsRegistryInterface::VisitAction::End:
-        {
-            if (valueName.starts_with(ACSNamePrefix))
-            {
-                AZ_Assert(!m_nameStack.empty(), "Name stack should not be empty. More stack pops, than pushes");
-                m_nameStack.pop();
-            }
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
         }
-        break;
 
-        default:
-            break;
-        }
+        AZStd::string name = visitArgs.m_fieldName.substr(ACSNamePrefix.size());
 
-        return AZ::SettingsRegistryInterface::VisitResponse::Continue;
-    }
+        AssetRecognizer& assetRecognizer = m_assetRecognizers.emplace_back();
+        assetRecognizer.m_name = name;
 
-    void ACSVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value)
-    {
-        auto* assetRecognizer = CurrentAssetRecognizer();
-        if (!assetRecognizer)
+        using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/lockSource"))
         {
-            return;
+            assetRecognizer.m_testLockSource = value;
         }
-        else if (valueName == "lockSource")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/critical"))
         {
-            assetRecognizer->m_testLockSource = value;
+            assetRecognizer.m_isCritical = value;
         }
-        else if (valueName == "critical")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/checkServer"))
         {
-            assetRecognizer->m_isCritical = value;
+            assetRecognizer.m_checkServer = value;
         }
-        else if (valueName == "checkServer")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/supportsCreateJobs"))
         {
-            assetRecognizer->m_checkServer = value;
+            assetRecognizer.m_supportsCreateJobs = value;
         }
-        else if (valueName == "supportsCreateJobs")
+        if (bool value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/outputProductDependencies"))
         {
-            assetRecognizer->m_supportsCreateJobs = value;
+            assetRecognizer.m_outputProductDependencies = value;
         }
-        else if (valueName == "outputProductDependencies")
-        {
-            assetRecognizer->m_outputProductDependencies = value;
-        }
-    }
 
-    void ACSVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
-    {
-        auto* assetRecognizer = CurrentAssetRecognizer();
-        if (!assetRecognizer)
+        if (AZ::s64 value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/priority"))
         {
-            return;
-        }
-        else if (valueName == "priority")
-        {
-            assetRecognizer->m_priority = aznumeric_cast<int>(value);
-        }
-    }
-
-    void ACSVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
-    {
-        auto* assetRecognizer = CurrentAssetRecognizer();
-        if (!assetRecognizer)
-        {
-            return;
+            assetRecognizer.m_priority = aznumeric_cast<int>(value);
         }
 
         // The "pattern" and "glob" entries were previously parsed by QSettings which un-escapes the values
@@ -814,38 +582,44 @@ namespace AssetProcessor
             return unescapedResult;
         };
 
-        if (valueName == "pattern")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/pattern"))
         {
             if (!value.empty())
             {
                 const auto patternType = AssetBuilderSDK::AssetBuilderPattern::Regex;
-                assetRecognizer->m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
+                assetRecognizer.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
             }
         }
-        else if (valueName == "glob")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/glob"))
         {
             // Add the glob pattern if it the matter matcher doesn't already contain a valid regex pattern
-            if (!assetRecognizer->m_patternMatcher.IsValid())
+            if (!assetRecognizer.m_patternMatcher.IsValid())
             {
                 const auto patternType = AssetBuilderSDK::AssetBuilderPattern::Wildcard;
-                assetRecognizer->m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
+                assetRecognizer.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
             }
         }
-        else if (valueName == "version")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/version"))
         {
-            assetRecognizer->m_version = value;
+            assetRecognizer.m_version = value;
         }
-        else if (valueName == "productAssetType")
+        if (AZStd::string value;
+            visitArgs.m_registry.Get(value, FixedValueString(visitArgs.m_jsonKeyPath) + "/productAssetType"))
         {
             if (!value.empty())
             {
                 AZ::Uuid productAssetType{ value.data(), value.size() };
                 if (!productAssetType.IsNull())
                 {
-                    assetRecognizer->m_productAssetType = productAssetType;
+                    assetRecognizer.m_productAssetType = productAssetType;
                 }
             }
         }
+
+        return AZ::SettingsRegistryInterface::VisitResponse::Skip;
     }
  
     const char AssetConfigPlatformDir[] = "AssetProcessorConfig/";
@@ -1488,7 +1262,7 @@ namespace AssetProcessor
             m_excludeAssetRecognizers[excludeRecognizer.m_name] = AZStd::move(excludeRecognizer);
         }
 
-        SimpleJobVisitor simpleJobVisitor(*settingsRegistry, m_enabledPlatforms);
+        SimpleJobVisitor simpleJobVisitor(m_enabledPlatforms);
         settingsRegistry->Visit(simpleJobVisitor, AssetProcessorSettingsKey);
         for (auto&& sjRecognizer : simpleJobVisitor.m_assetRecognizers)
         {

+ 1 - 107
Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h

@@ -19,6 +19,7 @@
 #include <QSet>
 
 #include <AzCore/Settings/SettingsRegistry.h>
+#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
 #include <AzCore/std/string/string.h>
 #include <native/utilities/assetUtils.h>
 #include <native/AssetManager/assetScanFolderInfo.h>
@@ -41,22 +42,6 @@ namespace AssetProcessor
     extern const char AssetConfigPlatformDir[];
     extern const char AssetProcessorPlatformConfigFileName[];
 
-    struct AssetImporterPathsVisitor
-        : AZ::SettingsRegistryInterface::Visitor
-    {
-        AssetImporterPathsVisitor(AZ::SettingsRegistryInterface* settingsRegistry, AZStd::vector<AZStd::string>& supportedExtension)
-            : m_settingsRegistry(settingsRegistry)
-            , m_supportedFileExtensions(supportedExtension)
-        {
-        }
-
-        using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-        AZ::SettingsRegistryInterface* m_settingsRegistry;
-        AZStd::vector<AZStd::string> m_supportedFileExtensions;
-    };
-
     //! Information for a given recognizer, on a specific platform
     //! essentially a plain data holder, but with helper funcs
     enum class AssetInternalSpec
@@ -139,97 +124,6 @@ namespace AssetProcessor
         virtual bool AddAssetCacheRecognizerContainer(const RecognizerContainer& recognizerContainer) = 0;
     };
 
-    //! Visitor for reading the "/Amazon/AssetProcessor/Settings/ScanFolder *" entries from the Settings Registry
-    //! Expects the key to path to the visitor to be "/Amazon/AssetProcessor/Settings"
-    struct ScanFolderVisitor
-        : AZ::SettingsRegistryInterface::Visitor
-    {
-        AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-            AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
-
-        using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-        struct ScanFolderInfo
-        {
-            AZStd::string m_scanFolderIdentifier;
-            AZStd::string m_scanFolderDisplayName;
-            AZ::IO::Path m_watchPath{ AZ::IO::PosixPathSeparator };
-            AZStd::vector<AZStd::string> m_includeIdentifiers;
-            AZStd::vector<AZStd::string> m_excludeIdentifiers;
-            int m_scanOrder{};
-            bool m_isRecursive{};
-        };
-        AZStd::vector<ScanFolderInfo> m_scanFolderInfos;
-    private:
-        AZStd::stack<AZStd::string> m_scanFolderStack;
-    };
-
-    struct ExcludeVisitor
-        : AZ::SettingsRegistryInterface::Visitor
-    {
-        AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-            AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
-
-        using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-        AZStd::vector<ExcludeAssetRecognizer> m_excludeAssetRecognizers;
-    private:
-        AZStd::stack<AZStd::string> m_excludeNameStack;
-    };
-
-    struct SimpleJobVisitor
-        : AZ::SettingsRegistryInterface::Visitor
-    {
-        SimpleJobVisitor(const AZ::SettingsRegistryInterface& settingsRegistry, const AZStd::vector<AssetBuilderSDK::PlatformInfo>& enabledPlatforms)
-            : m_registry(settingsRegistry)
-            , m_enabledPlatforms(enabledPlatforms)
-        {
-        }
-        AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-            AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
-
-        using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-        struct SimpleJobAssetRecognizer
-        {
-            AssetRecognizer m_recognizer;
-            AZStd::string m_defaultParams;
-            bool m_ignore{};
-        };
-        AZStd::vector<SimpleJobAssetRecognizer> m_assetRecognizers;
-    private:
-        void ApplyParamsOverrides(AZStd::string_view path);
-
-        AZStd::stack<AZStd::string> m_simpleJobNameStack;
-        const AZ::SettingsRegistryInterface& m_registry;
-        const AZStd::vector<AssetBuilderSDK::PlatformInfo>& m_enabledPlatforms;
-    };
-
-    //! This vistor reads in the Asset Cache Server configuration elements from the settings registry
-    struct ACSVisitor
-        : AZ::SettingsRegistryInterface::Visitor
-    {
-        AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
-            AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
-
-        using AZ::SettingsRegistryInterface::Visitor::Visit;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
-        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
-
-        AZStd::vector<AssetRecognizer> m_assetRecognizers;
-    private:
-        AssetRecognizer* CurrentAssetRecognizer();
-
-        AZStd::stack<AZStd::string> m_nameStack;
-    };
-
     /** Reads the platform ini configuration file to determine
     * platforms for which assets needs to be build
     */