[Tizen] Add DumpTree api 38/319738/1 accepted/tizen/9.0/unified/20250220.165936
authorYoungsun Suh <youngsun.suh@samsung.com>
Mon, 11 Nov 2024 01:30:42 +0000 (10:30 +0900)
committerYoungsun Suh <youngsun.suh@samsung.com>
Mon, 17 Feb 2025 06:17:05 +0000 (15:17 +0900)
Change-Id: I997178022d5ad97a632aed580fb01704ca1665d8

dali/devel-api/atspi-interfaces/accessible.h
dali/internal/accessibility/bridge/accessible.cpp
dali/internal/accessibility/bridge/bridge-accessible.cpp
dali/internal/accessibility/bridge/bridge-accessible.h
dali/internal/accessibility/bridge/dummy/dummy-atspi.cpp

index e14ffb67adc0d871c33c4edd9e2d8ea0791f860a..ae00f95bc2d349bdd54bee01d5a0177dda264fd7 100644 (file)
@@ -269,6 +269,20 @@ public:
     return mSuppressedEvents;
   }
 
+  enum class DumpDetailLevel
+  {
+    DUMP_SHORT              = 0,
+    DUMP_SHORT_SHOWING_ONLY = 1,
+    DUMP_FULL               = 2,
+    DUMP_FULL_SHOWING_ONLY  = 3,
+  };
+
+  /**
+   * @brief Dumps tree structure of accessible objects starting from self.
+   * @param [in] detailLevel Detail level of dumped json output.
+   */
+  std::string DumpTree(DumpDetailLevel detailLevel);
+
 protected:
   Accessible();
   Accessible(const Accessible&)         = delete;
index 6661e4f0bb45b82a7eb0dade66736456a913fd18..c03173f88961ad0d984fd515d299327d180ef261 100644 (file)
 //INTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/accessibility-bridge.h>
 #include <dali/devel-api/atspi-interfaces/accessible.h>
-#include <dali/devel-api/atspi-interfaces/socket.h>
+#include <dali/devel-api/atspi-interfaces/component.h>
+#include <dali/devel-api/atspi-interfaces/value.h>
 #include <dali/internal/accessibility/bridge/accessibility-common.h>
 
 using namespace Dali::Accessibility;
 
+namespace
+{
+constexpr const char* ESCAPED_QUOTE{"\""};
+constexpr const char* KEY_ROLE{"role"};
+constexpr const char* KEY_TEXT{"text"};
+constexpr const char* KEY_STATES{"states"};
+constexpr const char* KEY_TYPE{"type"};
+constexpr const char* KEY_AUTOMATION_ID{"automationId"};
+constexpr const char* KEY_ATTRS{"attributes"};
+constexpr const char* KEY_DESCRIPTION{"description"};
+constexpr const char* KEY_TOOLKIT{"toolkit"};
+constexpr const char* KEY_VALUE{"value"};
+constexpr const char* KEY_CHILDREN{"children"};
+constexpr const char* VAL_TOOLKIT{"dali"};
+
+// Function to escape special characters in a string
+std::string EscapeString(const std::string& input)
+{
+  std::string escaped;
+  escaped.reserve(input.length()); // Reserve space to avoid reallocations
+
+  for(char ch : input)
+  {
+    switch(ch)
+    {
+      case '\n':
+        escaped.append("\\n");
+        break;
+      case '\r':
+        escaped.append("\\r");
+        break;
+      case '\t':
+        escaped.append("\\t");
+        break;
+      case '\\':
+        escaped.append("\\\\");
+        break;
+      case '\"':
+        escaped.append("\\\"");
+        break;
+      default:
+        escaped.push_back(ch);
+        break;
+    }
+  }
+
+  return escaped;
+}
+
+// Helper function to quote strings properly for JSON output format.
+const auto Quote = [](const std::string& input, bool escape = false) -> std::string {
+  std::string escapedQuote{ESCAPED_QUOTE};
+  return escapedQuote + (escape ? EscapeString(input) : input) + ESCAPED_QUOTE;
+};
+
+// Helper function to check if we should include only showing nodes or not.
+const auto IncludeShowingOnly = [](Accessible::DumpDetailLevel detailLevel) -> bool {
+  return detailLevel == Accessible::DumpDetailLevel::DUMP_SHORT_SHOWING_ONLY || detailLevel == Accessible::DumpDetailLevel::DUMP_FULL_SHOWING_ONLY;
+};
+
+// Helper function to get type name from attributes map.
+const auto GetTypeString = [](const Attributes& attrs) -> std::string {
+  std::ostringstream msg;
+  if(auto iter = attrs.find("class"); iter != attrs.end())
+  {
+    msg << Quote(KEY_TYPE) << " : " << Quote(iter->second);
+  }
+  return msg.str();
+};
+
+// Helper function to get type name from attributes map.
+const auto GetAutomationIdString = [](const Attributes& attrs) -> std::string {
+  std::ostringstream msg;
+  if(auto iter = attrs.find(KEY_AUTOMATION_ID); iter != attrs.end())
+  {
+    msg << Quote(KEY_AUTOMATION_ID) << " : " << Quote(iter->second, true);
+  }
+  return msg.str();
+};
+
+// Helper function to get screen coordinates as a string.
+const auto GetScreenCoordString = [](Accessible* node) -> std::string {
+  std::ostringstream msg;
+  auto*              component = Component::DownCast(node);
+  if(component)
+  {
+    auto rect = component->GetExtents(CoordinateType::SCREEN);
+    msg << Quote("x") << ": " << rect.x << ", "
+        << Quote("y") << ": " << rect.y << ", "
+        << Quote("w") << ": " << rect.width << ", "
+        << Quote("h") << ": " << rect.height;
+  }
+  return msg.str();
+};
+
+// Helper function to get attributes map as a string.
+const auto GetOtherAttributesString = [](const Attributes& attrs) -> std::string {
+  std::ostringstream msg;
+  for(const auto& iter : attrs)
+  {
+    if(iter.first != "class" && iter.first != KEY_AUTOMATION_ID)
+    {
+      if(!msg.str().empty())
+      {
+        msg << ", ";
+      }
+      msg << Quote(iter.first) << ": " << Quote(iter.second, true);
+    }
+  }
+  return msg.str();
+};
+
+// Helper function to get value interface properties as a string. If there is no value interface, it will try to get value text instead.
+const auto GetValueString = [](Accessible* node) -> std::string {
+  std::ostringstream msg;
+  auto*              valueInterface = Value::DownCast(node);
+  if(valueInterface)
+  {
+    msg << Quote(KEY_VALUE) << ": { "
+        << Quote("current") << ": " << valueInterface->GetCurrent() << ", "
+        << Quote("min") << ": " << valueInterface->GetMinimum() << ", "
+        << Quote("max") << ": " << valueInterface->GetMaximum() << ", "
+        << Quote("increment") << ": " << valueInterface->GetMinimumIncrement()
+        << "}";
+  }
+  else if(auto valueText = node->GetValue(); !valueText.empty())
+  {
+    msg << Quote(KEY_VALUE) << ": " << Quote(valueText, true);
+  }
+  return msg.str();
+};
+
+// Recursive function to dump accessible tree as a JSON string.
+std::string DumpJson(Accessible* node, Accessible::DumpDetailLevel detailLevel, bool isRoot)
+{
+  if(!node)
+  {
+    return {};
+  }
+
+  const auto states    = node->GetStates();
+  const bool isShowing = states[State::SHOWING];
+  if(!isShowing && IncludeShowingOnly(detailLevel))
+  {
+    return {};
+  }
+
+  std::ostringstream msg;
+  msg << "{ " << Quote(KEY_ROLE) << ": " << Quote(node->GetRoleName()) << ", "
+      << Quote(KEY_STATES) << ": " << states.GetRawData64();
+
+  if(auto text = node->GetName(); !text.empty())
+  {
+    msg << ", " << Quote(KEY_TEXT) << ": " << Quote(text, true);
+  }
+
+  if(auto value = GetValueString(node); !value.empty())
+  {
+    msg << ", " << value;
+  }
+
+  const auto attributes = node->GetAttributes();
+  if(auto type = GetTypeString(attributes); !type.empty())
+  {
+    msg << ", " << type;
+  }
+
+  if(auto automationId = GetAutomationIdString(attributes); !automationId.empty())
+  {
+    msg << ", " << automationId;
+  }
+
+  if(auto screenCoord = GetScreenCoordString(node); !screenCoord.empty())
+  {
+    msg << ", " << screenCoord;
+  }
+
+  if(isRoot)
+  {
+    msg << ", " << Quote(KEY_TOOLKIT) << ": " << Quote(VAL_TOOLKIT);
+  }
+
+  if(detailLevel == Accessible::DumpDetailLevel::DUMP_FULL || detailLevel == Accessible::DumpDetailLevel::DUMP_FULL_SHOWING_ONLY)
+  {
+    if(auto otherAttrs = GetOtherAttributesString(attributes); !otherAttrs.empty())
+    {
+      msg << ", " << Quote(KEY_ATTRS) << ": { " << otherAttrs << " }";
+    }
+
+    if(auto description = node->GetDescription(); !description.empty())
+    {
+      msg << ", " << Quote(KEY_DESCRIPTION) << ": " << Quote(description, true);
+    }
+  }
+
+  auto children = node->GetChildren();
+  if(!children.empty())
+  {
+    msg << ", " << Quote(KEY_CHILDREN) << ": [ ";
+
+    // Recursively dump all the children as well
+    std::string childDump;
+    for(const auto& child : children)
+    {
+      auto curChildDump = DumpJson(child, detailLevel, false);
+      if(!curChildDump.empty())
+      {
+        if(!childDump.empty())
+        {
+          msg << ", ";
+        }
+        msg << curChildDump;
+        childDump = std::move(curChildDump);
+      }
+    }
+    msg << "]";
+  }
+  msg << " }";
+
+  return msg.str();
+}
+
+} // anonymous namespace
+
 Accessible::Accessible()
 {
 }
@@ -89,3 +314,8 @@ bool Accessible::IsProxy() const
 {
   return false;
 }
+
+std::string Accessible::DumpTree(DumpDetailLevel detailLevel)
+{
+  return DumpJson(this, detailLevel, true);
+}
\ No newline at end of file
index 10a7a41a7da91b524b04131e15b369ee1f5632e7..b70a08013bdc83a4a7dd02ed959db1e0339b8b62 100644 (file)
@@ -398,6 +398,7 @@ void BridgeAccessible::RegisterInterfaces()
   AddFunctionToInterface(desc, "GetRelationSet", &BridgeAccessible::GetRelationSet);
   AddFunctionToInterface(desc, "SetListenPostRender", &BridgeAccessible::SetListenPostRender);
   AddFunctionToInterface(desc, "GetNodeInfo", &BridgeAccessible::GetNodeInfo);
+  AddFunctionToInterface(desc, "DumpTree", &BridgeAccessible::DumpTree);
   mDbusServer.addInterface("/", desc, true);
 }
 
@@ -1096,3 +1097,9 @@ DBus::ValueOrError<void> BridgeAccessible::SetListenPostRender(bool enabled)
   FindSelf()->SetListenPostRender(enabled);
   return {};
 }
+
+DBus::ValueOrError<std::string> BridgeAccessible::DumpTree(Accessible::DumpDetailLevel detailLevel)
+{
+  DALI_LOG_RELEASE_INFO("####################### DumpTree #######################\n");
+  return FindSelf()->DumpTree(detailLevel);
+}
\ No newline at end of file
index 0f8533d99329c194afe953253c52f6e9fe574b93..a182a0f385cecff7ebf2f52d4f7fc26a30d67dda 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_ACCESSIBILITY_BRIDGE_ACCESSIBLE_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -148,6 +148,11 @@ public:
    */
   DBus::ValueOrError<std::string> GetRoleName();
 
+  /**
+   * @copydoc Dali::Accessibility::Accessible::DumpTree()
+   */
+  DBus::ValueOrError<std::string> DumpTree(Dali::Accessibility::Accessible::DumpDetailLevel detailLevel);
+
   /**
    * @copydoc Dali::Accessibility::Accessible::GetLocalizedRoleName()
    */
index 51c9ff3ae43793dd52b6f3a65962a9e7040b3ce6..8bfbb77c218d6d92be7d36044ece68e2f4343eeb 100644 (file)
@@ -41,6 +41,11 @@ Accessibility::Address Accessibility::Accessible::GetAddress() const
   return {};
 }
 
+std::string Accessibility::Accessible::DumpTree(Accessibility::Accessible::DumpDetailLevel detailLevel)
+{
+  return {};
+}
+
 std::shared_ptr<Accessibility::Bridge::Data> Accessibility::Accessible::GetBridgeData() const
 {
   return {};