[ATSPI] Introduce GetMatchesInMatches API
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-collection.cpp
index 022a310..2011130 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2021 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.
 
 // EXTERNAL INCLUDES
 #include <algorithm>
-#include <iostream>
 #include <unordered_set>
 #include <vector>
 
 using namespace Dali::Accessibility;
 
+namespace
+{
+/**
+ * @brief Enumeration used for quering Accessibility objects.
+ *
+ * Refer to MatchType enumeration.
+ */
+enum class AtspiCollection
+{
+  MATCH_INVALID,
+  MATCH_ALL,
+  MATCH_ANY,
+  MATCH_NONE,
+  MATCH_EMPTY,
+  MATCH_LAST_DEFINED,
+};
+} // anonymous namespace
+
 void BridgeCollection::RegisterInterfaces()
 {
-  DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceCollection};
-  AddFunctionToInterface( desc, "GetMatches", &BridgeCollection::GetMatches );
-  dbusServer.addInterface( "/", desc, true );
+  DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::COLLECTION)};
+  AddFunctionToInterface(desc, "GetMatches", &BridgeCollection::GetMatches);
+  AddFunctionToInterface(desc, "GetMatchesInMatches", &BridgeCollection::GetMatchesInMatches);
+
+  mDbusServer.addInterface("/", desc, true);
 }
 
 Collection* BridgeCollection::FindSelf() const
 {
-  auto s = BridgeBase::FindSelf();
-  assert( s );
-  auto s2 = dynamic_cast< Collection* >( s );
-  if( !s2 )
-    throw std::domain_error{"object " + s->GetAddress().ToString() + " doesn't have Collection interface"};
-  return s2;
+  return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::COLLECTION>();
 }
 
-enum
-{
-  ATSPI_Collection_MATCH_INVALID,
-  ATSPI_Collection_MATCH_ALL,
-  ATSPI_Collection_MATCH_ANY,
-  ATSPI_Collection_MATCH_NONE,
-  ATSPI_Collection_MATCH_EMPTY,
-  ATSPI_Collection_MATCH_LAST_DEFINED,
-};
-
+/**
+ * @brief The BridgeCollection::Comparer structure.
+ *
+ * Once the data is de-serialized by DBusWrapper, the data of match rule is passed
+ * to Comparer type which do the comparison against a single accessible object.
+ */
 struct BridgeCollection::Comparer
 {
   using Mode = MatchType;
 
+  /**
+   * @brief Enumeration to check the object is found first.
+   */
   enum class CompareFuncExit
   {
     FIRST_FOUND,
     FIRST_NOT_FOUND
   };
 
-  static Mode ConvertToMatchType( int32_t mode )
+  static Mode ConvertToMatchType(int32_t mode)
   {
-    switch( mode )
+    switch(mode)
     {
-      case ATSPI_Collection_MATCH_INVALID:
+      case static_cast<int32_t>(AtspiCollection::MATCH_INVALID):
       {
         return Mode::INVALID;
       }
-      case ATSPI_Collection_MATCH_ALL:
+      case static_cast<int32_t>(AtspiCollection::MATCH_ALL):
       {
         return Mode::ALL;
       }
-      case ATSPI_Collection_MATCH_ANY:
+      case static_cast<int32_t>(AtspiCollection::MATCH_ANY):
       {
         return Mode::ANY;
       }
-      case ATSPI_Collection_MATCH_NONE:
+      case static_cast<int32_t>(AtspiCollection::MATCH_NONE):
       {
         return Mode::NONE;
       }
-      case ATSPI_Collection_MATCH_EMPTY:
+      case static_cast<int32_t>(AtspiCollection::MATCH_EMPTY):
       {
         return Mode::EMPTY;
       }
@@ -91,91 +104,148 @@ struct BridgeCollection::Comparer
     return Mode::INVALID;
   }
 
+  /**
+   * @brief The ComparerInterfaces structure
+   */
   struct ComparerInterfaces
   {
-    std::unordered_set< std::string > object;
-    std::vector< std::string > requested;
-    Mode mode = Mode::INVALID;
+    std::unordered_set<std::string> mObject;
+    std::vector<std::string>        mRequested;
+    Mode                            mMode = Mode::INVALID;
 
-    ComparerInterfaces( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::InterfacesMatchType >( *rule ) ) )
+    ComparerInterfaces(MatchRule* rule)
+    : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::INTERFACES_MATCH_TYPE)>(*rule)))
     {
-      requested = {std::get< Index::Interfaces >( *rule ).begin(), std::get< Index::Interfaces >( *rule ).end()};
+      mRequested = {std::get<static_cast<std::size_t>(Index::INTERFACES)>(*rule).begin(), std::get<static_cast<std::size_t>(Index::INTERFACES)>(*rule).end()};
     }
-    void Update( Accessible* obj )
+
+    void Update(Accessible* obj)
     {
-      object.clear();
-      for( auto& q : obj->GetInterfaces() )
-        object.insert( std::move( q ) );
+      mObject.clear();
+      for(auto& interface : obj->GetInterfacesAsStrings())
+      {
+        mObject.insert(std::move(interface));
+      }
     }
-    bool RequestEmpty() const { return requested.empty(); }
-    bool ObjectEmpty() const { return object.empty(); }
-    bool Compare( CompareFuncExit exit )
+
+    bool IsRequestEmpty() const
+    {
+      return mRequested.empty();
+    }
+
+    bool IsObjectEmpty() const
+    {
+      return mObject.empty();
+    }
+
+    bool Compare(CompareFuncExit exit)
     {
       bool foundAny = false;
-      for( auto& iname : requested )
+      for(auto& iname : mRequested)
       {
-        bool found = ( object.find( iname ) != object.end() );
-        if( found )
+        bool found = (mObject.find(iname) != mObject.end());
+        if(found)
+        {
           foundAny = true;
-        if( found == ( exit == CompareFuncExit::FIRST_FOUND ) )
+        }
+
+        if(found == (exit == CompareFuncExit::FIRST_FOUND))
+        {
           return found;
+        }
       }
       return foundAny;
     }
-  };
+  }; // ComparerInterfaces struct
+
+  /**
+   * @brief The ComparerAttributes structure
+   */
   struct ComparerAttributes
   {
-    std::unordered_map< std::string, std::string > requested, object;
-    Mode mode = Mode::INVALID;
+    std::unordered_map<std::string, std::string> mRequested;
+    std::unordered_map<std::string, std::string> mObject;
+    Mode                                         mMode = Mode::INVALID;
+
+    ComparerAttributes(MatchRule* rule)
+    : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::ATTRIBUTES_MATCH_TYPE)>(*rule)))
+    {
+      mRequested = std::get<static_cast<std::size_t>(Index::ATTRIBUTES)>(*rule);
+    }
+
+    void Update(Accessible* obj)
+    {
+      mObject = obj->GetAttributes();
+    }
 
-    ComparerAttributes( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::AttributesMatchType >( *rule ) ) )
+    bool IsRequestEmpty() const
     {
-      requested = std::get< Index::Attributes >( *rule );
+      return mRequested.empty();
     }
-    void Update( Accessible* obj )
+
+    bool IsObjectEmpty() const
     {
-      object = obj->GetAttributes();
+      return mObject.empty();
     }
-    bool RequestEmpty() const { return requested.empty(); }
-    bool ObjectEmpty() const { return object.empty(); }
-    bool Compare( CompareFuncExit exit )
+
+    bool Compare(CompareFuncExit exit)
     {
       bool foundAny = false;
-      for( auto& iname : requested )
+      for(auto& iname : mRequested)
       {
-        auto it = object.find( iname.first );
-        bool found = it != object.end() && iname.second == it->second;
-        if( found )
+        auto it    = mObject.find(iname.first);
+        bool found = it != mObject.end() && iname.second == it->second;
+        if(found)
+        {
           foundAny = true;
-        if( found == ( exit == CompareFuncExit::FIRST_FOUND ) )
+        }
+
+        if(found == (exit == CompareFuncExit::FIRST_FOUND))
         {
           return found;
         }
       }
       return foundAny;
     }
-  };
+  }; // ComparerAttributes struct
+
+  /**
+   * @brief The ComparerRoles structure
+   */
   struct ComparerRoles
   {
-    using Roles = BitSets< 4, Role >;
-    Roles requested, object;
-    Mode mode = Mode::INVALID;
+    using Roles = EnumBitSet<Role, Role::MAX_COUNT>;
 
-    ComparerRoles( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::RolesMatchType >( *rule ) ) )
+    Roles mRequested;
+    Roles mObject;
+    Mode  mMode = Mode::INVALID;
+
+    ComparerRoles(MatchRule* rule)
+    : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::ROLES_MATCH_TYPE)>(*rule)))
+    {
+      mRequested = Roles{std::get<static_cast<std::size_t>(Index::ROLES)>(*rule)};
+    }
+
+    void Update(Accessible* obj)
     {
-      requested = Roles{std::get< Index::Roles >( *rule )};
+      mObject                 = {};
+      mObject[obj->GetRole()] = true;
+      assert(mObject);
     }
-    void Update( Accessible* obj )
+
+    bool IsRequestEmpty() const
     {
-      object = {};
-      object[obj->GetRole()] = true;
-      assert( object );
+      return !mRequested;
     }
-    bool RequestEmpty() const { return !requested; }
-    bool ObjectEmpty() const { return !object; }
-    bool Compare( CompareFuncExit exit )
+
+    bool IsObjectEmpty() const
     {
-      switch( mode )
+      return !mObject;
+    }
+
+    bool Compare(CompareFuncExit exit)
+    {
+      switch(mMode)
       {
         case Mode::INVALID:
         {
@@ -184,38 +254,54 @@ struct BridgeCollection::Comparer
         case Mode::EMPTY:
         case Mode::ALL:
         {
-          return requested == ( object & requested );
+          return mRequested == (mObject & mRequested);
         }
         case Mode::ANY:
         {
-          return bool( object & requested );
+          return bool(mObject & mRequested);
         }
         case Mode::NONE:
         {
-          return bool( object & requested );
+          return bool(mObject & mRequested);
         }
       }
       return false;
     }
-  };
+  }; // ComparerRoles struct
+
+  /**
+   * @brief The ComparerStates structure
+   */
   struct ComparerStates
   {
-    States requested, object;
-    Mode mode = Mode::INVALID;
+    States mRequested;
+    States mObject;
+    Mode   mMode = Mode::INVALID;
+
+    ComparerStates(MatchRule* rule)
+    : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::STATES_MATCH_TYPE)>(*rule)))
+    {
+      mRequested = States{std::get<static_cast<std::size_t>(Index::STATES)>(*rule)};
+    }
+
+    void Update(Accessible* obj)
+    {
+      mObject = obj->GetStates();
+    }
 
-    ComparerStates( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::StatesMatchType >( *rule ) ) )
+    bool IsRequestEmpty() const
     {
-      requested = States{std::get< Index::States >( *rule )};
+      return !mRequested;
     }
-    void Update( Accessible* obj )
+
+    bool IsObjectEmpty() const
     {
-      object = obj->GetStates();
+      return !mObject;
     }
-    bool RequestEmpty() const { return !requested; }
-    bool ObjectEmpty() const { return !object; }
-    bool Compare( CompareFuncExit exit )
+
+    bool Compare(CompareFuncExit exit)
     {
-      switch( mode )
+      switch(mMode)
       {
         case Mode::INVALID:
         {
@@ -224,55 +310,70 @@ struct BridgeCollection::Comparer
         case Mode::EMPTY:
         case Mode::ALL:
         {
-          return requested == ( object & requested );
+          return mRequested == (mObject & mRequested);
         }
         case Mode::ANY:
         {
-          return bool( object & requested );
+          return bool(mObject & mRequested);
         }
         case Mode::NONE:
         {
-          return bool( object & requested );
+          return bool(mObject & mRequested);
         }
       }
       return false;
     }
-  };
+  }; // ComparerStates struct
 
-  template < typename T >
-  bool compareFunc( T& cmp, Accessible* obj )
+  template<typename T>
+  bool CompareFunc(T& cmp, Accessible* obj)
   {
-    if( cmp.mode == Mode::INVALID )
+    if(cmp.mMode == Mode::INVALID)
+    {
       return true;
-    cmp.Update( obj );
-    switch( cmp.mode )
+    }
+
+    cmp.Update(obj);
+    switch(cmp.mMode)
     {
       case Mode::ANY:
       {
-        if( cmp.RequestEmpty() || cmp.ObjectEmpty() )
+        if(cmp.IsRequestEmpty() || cmp.IsObjectEmpty())
+        {
           return false;
+        }
         break;
       }
       case Mode::ALL:
       {
-        if( cmp.RequestEmpty() )
+        if(cmp.IsRequestEmpty())
+        {
           return true;
-        if( cmp.ObjectEmpty() )
+        }
+        if(cmp.IsObjectEmpty())
+        {
           return false;
+        }
         break;
       }
       case Mode::NONE:
       {
-        if( cmp.RequestEmpty() || cmp.ObjectEmpty() )
+        if(cmp.IsRequestEmpty() || cmp.IsObjectEmpty())
+        {
           return true;
+        }
         break;
       }
       case Mode::EMPTY:
       {
-        if( cmp.RequestEmpty() && cmp.ObjectEmpty() )
+        if(cmp.IsRequestEmpty() && cmp.IsObjectEmpty())
+        {
           return true;
-        if( cmp.RequestEmpty() || cmp.ObjectEmpty() )
+        }
+        if(cmp.IsRequestEmpty() || cmp.IsObjectEmpty())
+        {
           return false;
+        }
         break;
       }
       case Mode::INVALID:
@@ -281,25 +382,31 @@ struct BridgeCollection::Comparer
       }
     }
 
-    switch( cmp.mode )
+    switch(cmp.mMode)
     {
       case Mode::EMPTY:
       case Mode::ALL:
       {
-        if( !cmp.Compare( CompareFuncExit::FIRST_NOT_FOUND ) )
+        if(!cmp.Compare(CompareFuncExit::FIRST_NOT_FOUND))
+        {
           return false;
+        }
         break;
       }
       case Mode::ANY:
       {
-        if( cmp.Compare( CompareFuncExit::FIRST_FOUND ) )
+        if(cmp.Compare(CompareFuncExit::FIRST_FOUND))
+        {
           return true;
+        }
         break;
       }
       case Mode::NONE:
       {
-        if( cmp.Compare( CompareFuncExit::FIRST_FOUND ) )
+        if(cmp.Compare(CompareFuncExit::FIRST_FOUND))
+        {
           return false;
+        }
         break;
       }
       case Mode::INVALID:
@@ -307,7 +414,8 @@ struct BridgeCollection::Comparer
         return true;
       }
     }
-    switch( cmp.mode )
+
+    switch(cmp.mMode)
     {
       case Mode::EMPTY:
       case Mode::ALL:
@@ -327,42 +435,117 @@ struct BridgeCollection::Comparer
     return false;
   }
 
-  ComparerInterfaces ci;
-  ComparerAttributes ca;
-  ComparerRoles cr;
-  ComparerStates cs;
+  Comparer(MatchRule* rule)
+  : mInterface(rule),
+    mAttribute(rule),
+    mRole(rule),
+    mState(rule)
+  {
+  }
 
-  Comparer( MatchRule* mr ) : ci( mr ), ca( mr ), cr( mr ), cs( mr ) {}
+  bool operator()(Accessible* obj)
+  {
+    return CompareFunc(mInterface, obj) &&
+           CompareFunc(mAttribute, obj) &&
+           CompareFunc(mRole, obj) &&
+           CompareFunc(mState, obj);
+  }
 
-  bool operator()( Accessible* obj )
+  bool IsShowing(Accessible* obj)
   {
-    return compareFunc( ci, obj ) &&
-           compareFunc( ca, obj ) &&
-           compareFunc( cr, obj ) &&
-           compareFunc( cs, obj );
+    if (mState.mMode == Mode::NONE) return true;
+    mState.Update(obj);
+    if (mState.IsRequestEmpty() || mState.IsObjectEmpty()) return true;
+    if (!mState.mRequested[State::SHOWING] ) return true;
+    if (mState.mObject[State::SHOWING]) return true;
+
+    return false;
   }
-};
 
-void BridgeCollection::VisitNodes( Accessible* obj, std::vector< Accessible* >& result, Comparer& cmp, size_t maxCount )
+  ComparerInterfaces mInterface;
+  ComparerAttributes mAttribute;
+  ComparerRoles      mRole;
+  ComparerStates     mState;
+}; // BridgeCollection::Comparer struct
+
+
+void BridgeCollection::VisitNodes(Accessible* obj, std::vector<Accessible*>& result, Comparer& comparer, size_t maxCount)
 {
-  if( maxCount > 0 && result.size() >= maxCount )
+  if(maxCount > 0 && result.size() >= maxCount)
+  {
+    return;
+  }
+
+  if(comparer(obj))
+  {
+    result.emplace_back(obj);
+    // the code below will never return for maxCount equal 0
+    if(result.size() == maxCount)
+    {
+      return;
+    }
+  }
+
+  if (!comparer.IsShowing(obj))
+  {
     return;
+  }
 
-  if( cmp( obj ) )
-    result.emplace_back( obj );
+  for(auto i = 0u; i < obj->GetChildCount(); ++i)
+  {
+    VisitNodes(obj->GetChildAtIndex(i), result, comparer, maxCount);
+  }
+}
 
-  for( auto i = 0u; i < obj->GetChildCount(); ++i )
-    VisitNodes( obj->GetChildAtIndex( i ), result, cmp, maxCount );
+DBus::ValueOrError<std::vector<Accessible*> > BridgeCollection::GetMatches(MatchRule rule, uint32_t sortBy, int32_t count, bool traverse)
+{
+  std::vector<Accessible*> res;
+  auto                     self    = BridgeBase::FindCurrentObject();
+  auto                     matcher = Comparer{&rule};
+  VisitNodes(self, res, matcher, count);
+
+  switch(static_cast<SortOrder>(sortBy))
+  {
+    case SortOrder::CANONICAL:
+    {
+      break;
+    }
+
+    case SortOrder::REVERSE_CANONICAL:
+    {
+      std::reverse(res.begin(), res.end());
+      break;
+    }
+
+    default:
+    {
+      throw std::domain_error{"unsupported sorting order"};
+    }
+      //TODO: other cases
+  }
+
+  return res;
 }
 
-DBus::ValueOrError< std::vector< Accessible* > > BridgeCollection::GetMatches( MatchRule rule, uint32_t sortBy, int32_t count, bool traverse )
+DBus::ValueOrError<std::vector<Accessible*> > BridgeCollection::GetMatchesInMatches(MatchRule firstRule, MatchRule secondRule, uint32_t sortBy, int32_t firstCount, int32_t secondCount, bool traverse)
 {
-  std::vector< Accessible* > res;
-  auto self = BridgeBase::FindSelf();
-  auto matcher = Comparer{&rule};
-  VisitNodes( self, res, matcher, count );
+  std::vector<Accessible*> res;
+  std::vector<Accessible*> firstRes;
+  std::vector<Accessible*> secondRes;
+  auto                     self          = BridgeBase::FindCurrentObject();
+  auto                     firstMatcher  = Comparer{&firstRule};
+  auto                     secondMatcher = Comparer{&secondRule};
+  VisitNodes(self, firstRes, firstMatcher, firstCount);
+
+  for (auto &obj : firstRes)
+  {
+    VisitNodes(obj, secondRes, secondMatcher, secondCount);
+
+    res.insert(res.end(), secondRes.begin(), secondRes.end());
+    secondRes.clear();
+  }
 
-  switch( static_cast< SortOrder >( sortBy ) )
+  switch(static_cast<SortOrder>(sortBy))
   {
     case SortOrder::CANONICAL:
     {
@@ -371,7 +554,7 @@ DBus::ValueOrError< std::vector< Accessible* > > BridgeCollection::GetMatches( M
 
     case SortOrder::REVERSE_CANONICAL:
     {
-      std::reverse( res.begin(), res.end() );
+      std::reverse(res.begin(), res.end());
       break;
     }