[ATSPI] Introduce GetMatchesInMatches API
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-collection.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/accessibility/bridge/bridge-collection.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <unordered_set>
24 #include <vector>
25
26 using namespace Dali::Accessibility;
27
28 namespace
29 {
30 /**
31  * @brief Enumeration used for quering Accessibility objects.
32  *
33  * Refer to MatchType enumeration.
34  */
35 enum class AtspiCollection
36 {
37   MATCH_INVALID,
38   MATCH_ALL,
39   MATCH_ANY,
40   MATCH_NONE,
41   MATCH_EMPTY,
42   MATCH_LAST_DEFINED,
43 };
44 } // anonymous namespace
45
46 void BridgeCollection::RegisterInterfaces()
47 {
48   DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::COLLECTION)};
49   AddFunctionToInterface(desc, "GetMatches", &BridgeCollection::GetMatches);
50   AddFunctionToInterface(desc, "GetMatchesInMatches", &BridgeCollection::GetMatchesInMatches);
51
52   mDbusServer.addInterface("/", desc, true);
53 }
54
55 Collection* BridgeCollection::FindSelf() const
56 {
57   return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::COLLECTION>();
58 }
59
60 /**
61  * @brief The BridgeCollection::Comparer structure.
62  *
63  * Once the data is de-serialized by DBusWrapper, the data of match rule is passed
64  * to Comparer type which do the comparison against a single accessible object.
65  */
66 struct BridgeCollection::Comparer
67 {
68   using Mode = MatchType;
69
70   /**
71    * @brief Enumeration to check the object is found first.
72    */
73   enum class CompareFuncExit
74   {
75     FIRST_FOUND,
76     FIRST_NOT_FOUND
77   };
78
79   static Mode ConvertToMatchType(int32_t mode)
80   {
81     switch(mode)
82     {
83       case static_cast<int32_t>(AtspiCollection::MATCH_INVALID):
84       {
85         return Mode::INVALID;
86       }
87       case static_cast<int32_t>(AtspiCollection::MATCH_ALL):
88       {
89         return Mode::ALL;
90       }
91       case static_cast<int32_t>(AtspiCollection::MATCH_ANY):
92       {
93         return Mode::ANY;
94       }
95       case static_cast<int32_t>(AtspiCollection::MATCH_NONE):
96       {
97         return Mode::NONE;
98       }
99       case static_cast<int32_t>(AtspiCollection::MATCH_EMPTY):
100       {
101         return Mode::EMPTY;
102       }
103     }
104     return Mode::INVALID;
105   }
106
107   /**
108    * @brief The ComparerInterfaces structure
109    */
110   struct ComparerInterfaces
111   {
112     std::unordered_set<std::string> mObject;
113     std::vector<std::string>        mRequested;
114     Mode                            mMode = Mode::INVALID;
115
116     ComparerInterfaces(MatchRule* rule)
117     : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::INTERFACES_MATCH_TYPE)>(*rule)))
118     {
119       mRequested = {std::get<static_cast<std::size_t>(Index::INTERFACES)>(*rule).begin(), std::get<static_cast<std::size_t>(Index::INTERFACES)>(*rule).end()};
120     }
121
122     void Update(Accessible* obj)
123     {
124       mObject.clear();
125       for(auto& interface : obj->GetInterfacesAsStrings())
126       {
127         mObject.insert(std::move(interface));
128       }
129     }
130
131     bool IsRequestEmpty() const
132     {
133       return mRequested.empty();
134     }
135
136     bool IsObjectEmpty() const
137     {
138       return mObject.empty();
139     }
140
141     bool Compare(CompareFuncExit exit)
142     {
143       bool foundAny = false;
144       for(auto& iname : mRequested)
145       {
146         bool found = (mObject.find(iname) != mObject.end());
147         if(found)
148         {
149           foundAny = true;
150         }
151
152         if(found == (exit == CompareFuncExit::FIRST_FOUND))
153         {
154           return found;
155         }
156       }
157       return foundAny;
158     }
159   }; // ComparerInterfaces struct
160
161   /**
162    * @brief The ComparerAttributes structure
163    */
164   struct ComparerAttributes
165   {
166     std::unordered_map<std::string, std::string> mRequested;
167     std::unordered_map<std::string, std::string> mObject;
168     Mode                                         mMode = Mode::INVALID;
169
170     ComparerAttributes(MatchRule* rule)
171     : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::ATTRIBUTES_MATCH_TYPE)>(*rule)))
172     {
173       mRequested = std::get<static_cast<std::size_t>(Index::ATTRIBUTES)>(*rule);
174     }
175
176     void Update(Accessible* obj)
177     {
178       mObject = obj->GetAttributes();
179     }
180
181     bool IsRequestEmpty() const
182     {
183       return mRequested.empty();
184     }
185
186     bool IsObjectEmpty() const
187     {
188       return mObject.empty();
189     }
190
191     bool Compare(CompareFuncExit exit)
192     {
193       bool foundAny = false;
194       for(auto& iname : mRequested)
195       {
196         auto it    = mObject.find(iname.first);
197         bool found = it != mObject.end() && iname.second == it->second;
198         if(found)
199         {
200           foundAny = true;
201         }
202
203         if(found == (exit == CompareFuncExit::FIRST_FOUND))
204         {
205           return found;
206         }
207       }
208       return foundAny;
209     }
210   }; // ComparerAttributes struct
211
212   /**
213    * @brief The ComparerRoles structure
214    */
215   struct ComparerRoles
216   {
217     using Roles = EnumBitSet<Role, Role::MAX_COUNT>;
218
219     Roles mRequested;
220     Roles mObject;
221     Mode  mMode = Mode::INVALID;
222
223     ComparerRoles(MatchRule* rule)
224     : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::ROLES_MATCH_TYPE)>(*rule)))
225     {
226       mRequested = Roles{std::get<static_cast<std::size_t>(Index::ROLES)>(*rule)};
227     }
228
229     void Update(Accessible* obj)
230     {
231       mObject                 = {};
232       mObject[obj->GetRole()] = true;
233       assert(mObject);
234     }
235
236     bool IsRequestEmpty() const
237     {
238       return !mRequested;
239     }
240
241     bool IsObjectEmpty() const
242     {
243       return !mObject;
244     }
245
246     bool Compare(CompareFuncExit exit)
247     {
248       switch(mMode)
249       {
250         case Mode::INVALID:
251         {
252           return true;
253         }
254         case Mode::EMPTY:
255         case Mode::ALL:
256         {
257           return mRequested == (mObject & mRequested);
258         }
259         case Mode::ANY:
260         {
261           return bool(mObject & mRequested);
262         }
263         case Mode::NONE:
264         {
265           return bool(mObject & mRequested);
266         }
267       }
268       return false;
269     }
270   }; // ComparerRoles struct
271
272   /**
273    * @brief The ComparerStates structure
274    */
275   struct ComparerStates
276   {
277     States mRequested;
278     States mObject;
279     Mode   mMode = Mode::INVALID;
280
281     ComparerStates(MatchRule* rule)
282     : mMode(ConvertToMatchType(std::get<static_cast<std::size_t>(Index::STATES_MATCH_TYPE)>(*rule)))
283     {
284       mRequested = States{std::get<static_cast<std::size_t>(Index::STATES)>(*rule)};
285     }
286
287     void Update(Accessible* obj)
288     {
289       mObject = obj->GetStates();
290     }
291
292     bool IsRequestEmpty() const
293     {
294       return !mRequested;
295     }
296
297     bool IsObjectEmpty() const
298     {
299       return !mObject;
300     }
301
302     bool Compare(CompareFuncExit exit)
303     {
304       switch(mMode)
305       {
306         case Mode::INVALID:
307         {
308           return true;
309         }
310         case Mode::EMPTY:
311         case Mode::ALL:
312         {
313           return mRequested == (mObject & mRequested);
314         }
315         case Mode::ANY:
316         {
317           return bool(mObject & mRequested);
318         }
319         case Mode::NONE:
320         {
321           return bool(mObject & mRequested);
322         }
323       }
324       return false;
325     }
326   }; // ComparerStates struct
327
328   template<typename T>
329   bool CompareFunc(T& cmp, Accessible* obj)
330   {
331     if(cmp.mMode == Mode::INVALID)
332     {
333       return true;
334     }
335
336     cmp.Update(obj);
337     switch(cmp.mMode)
338     {
339       case Mode::ANY:
340       {
341         if(cmp.IsRequestEmpty() || cmp.IsObjectEmpty())
342         {
343           return false;
344         }
345         break;
346       }
347       case Mode::ALL:
348       {
349         if(cmp.IsRequestEmpty())
350         {
351           return true;
352         }
353         if(cmp.IsObjectEmpty())
354         {
355           return false;
356         }
357         break;
358       }
359       case Mode::NONE:
360       {
361         if(cmp.IsRequestEmpty() || cmp.IsObjectEmpty())
362         {
363           return true;
364         }
365         break;
366       }
367       case Mode::EMPTY:
368       {
369         if(cmp.IsRequestEmpty() && cmp.IsObjectEmpty())
370         {
371           return true;
372         }
373         if(cmp.IsRequestEmpty() || cmp.IsObjectEmpty())
374         {
375           return false;
376         }
377         break;
378       }
379       case Mode::INVALID:
380       {
381         return true;
382       }
383     }
384
385     switch(cmp.mMode)
386     {
387       case Mode::EMPTY:
388       case Mode::ALL:
389       {
390         if(!cmp.Compare(CompareFuncExit::FIRST_NOT_FOUND))
391         {
392           return false;
393         }
394         break;
395       }
396       case Mode::ANY:
397       {
398         if(cmp.Compare(CompareFuncExit::FIRST_FOUND))
399         {
400           return true;
401         }
402         break;
403       }
404       case Mode::NONE:
405       {
406         if(cmp.Compare(CompareFuncExit::FIRST_FOUND))
407         {
408           return false;
409         }
410         break;
411       }
412       case Mode::INVALID:
413       {
414         return true;
415       }
416     }
417
418     switch(cmp.mMode)
419     {
420       case Mode::EMPTY:
421       case Mode::ALL:
422       case Mode::NONE:
423       {
424         return true;
425       }
426       case Mode::ANY:
427       {
428         return false;
429       }
430       case Mode::INVALID:
431       {
432         return true;
433       }
434     }
435     return false;
436   }
437
438   Comparer(MatchRule* rule)
439   : mInterface(rule),
440     mAttribute(rule),
441     mRole(rule),
442     mState(rule)
443   {
444   }
445
446   bool operator()(Accessible* obj)
447   {
448     return CompareFunc(mInterface, obj) &&
449            CompareFunc(mAttribute, obj) &&
450            CompareFunc(mRole, obj) &&
451            CompareFunc(mState, obj);
452   }
453
454   bool IsShowing(Accessible* obj)
455   {
456     if (mState.mMode == Mode::NONE) return true;
457     mState.Update(obj);
458     if (mState.IsRequestEmpty() || mState.IsObjectEmpty()) return true;
459     if (!mState.mRequested[State::SHOWING] ) return true;
460     if (mState.mObject[State::SHOWING]) return true;
461
462     return false;
463   }
464
465   ComparerInterfaces mInterface;
466   ComparerAttributes mAttribute;
467   ComparerRoles      mRole;
468   ComparerStates     mState;
469 }; // BridgeCollection::Comparer struct
470
471
472 void BridgeCollection::VisitNodes(Accessible* obj, std::vector<Accessible*>& result, Comparer& comparer, size_t maxCount)
473 {
474   if(maxCount > 0 && result.size() >= maxCount)
475   {
476     return;
477   }
478
479   if(comparer(obj))
480   {
481     result.emplace_back(obj);
482     // the code below will never return for maxCount equal 0
483     if(result.size() == maxCount)
484     {
485       return;
486     }
487   }
488
489   if (!comparer.IsShowing(obj))
490   {
491     return;
492   }
493
494   for(auto i = 0u; i < obj->GetChildCount(); ++i)
495   {
496     VisitNodes(obj->GetChildAtIndex(i), result, comparer, maxCount);
497   }
498 }
499
500 DBus::ValueOrError<std::vector<Accessible*> > BridgeCollection::GetMatches(MatchRule rule, uint32_t sortBy, int32_t count, bool traverse)
501 {
502   std::vector<Accessible*> res;
503   auto                     self    = BridgeBase::FindCurrentObject();
504   auto                     matcher = Comparer{&rule};
505   VisitNodes(self, res, matcher, count);
506
507   switch(static_cast<SortOrder>(sortBy))
508   {
509     case SortOrder::CANONICAL:
510     {
511       break;
512     }
513
514     case SortOrder::REVERSE_CANONICAL:
515     {
516       std::reverse(res.begin(), res.end());
517       break;
518     }
519
520     default:
521     {
522       throw std::domain_error{"unsupported sorting order"};
523     }
524       //TODO: other cases
525   }
526
527   return res;
528 }
529
530 DBus::ValueOrError<std::vector<Accessible*> > BridgeCollection::GetMatchesInMatches(MatchRule firstRule, MatchRule secondRule, uint32_t sortBy, int32_t firstCount, int32_t secondCount, bool traverse)
531 {
532   std::vector<Accessible*> res;
533   std::vector<Accessible*> firstRes;
534   std::vector<Accessible*> secondRes;
535   auto                     self          = BridgeBase::FindCurrentObject();
536   auto                     firstMatcher  = Comparer{&firstRule};
537   auto                     secondMatcher = Comparer{&secondRule};
538   VisitNodes(self, firstRes, firstMatcher, firstCount);
539
540   for (auto &obj : firstRes)
541   {
542     VisitNodes(obj, secondRes, secondMatcher, secondCount);
543
544     res.insert(res.end(), secondRes.begin(), secondRes.end());
545     secondRes.clear();
546   }
547
548   switch(static_cast<SortOrder>(sortBy))
549   {
550     case SortOrder::CANONICAL:
551     {
552       break;
553     }
554
555     case SortOrder::REVERSE_CANONICAL:
556     {
557       std::reverse(res.begin(), res.end());
558       break;
559     }
560
561     default:
562     {
563       throw std::domain_error{"unsupported sorting order"};
564     }
565       //TODO: other cases
566   }
567
568   return res;
569 }