2826febb397188df5a85ccdaa624ed5eed434c9e
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / collection-adaptor.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2007 IBM Corp.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* collection.c: implements the Collection interface */
24
25 #include <string.h>
26
27 #include <atk/atk.h>
28 #include <droute/droute.h>
29
30 #include "accessible-register.h"
31 #include "accessible-marshaller.h"
32
33 #include "spi-common/bitarray.h"
34 #include "spi-common/spi-dbus.h"
35
36 typedef struct _MatchRulePrivate MatchRulePrivate;
37 struct _MatchRulePrivate
38 {
39   gint *states;
40      Accessibility_Collection_MatchType statematchtype;
41   AtkAttributeSet *attributes;
42      Accessibility_Collection_MatchType attributematchtype;
43   gint *roles;
44      Accessibility_Collection_MatchType rolematchtype;
45      gchar **ifaces;
46      Accessibility_Collection_MatchType interfacematchtype;
47      gboolean invert;
48 };
49
50 static gboolean
51 child_interface_p (AtkObject *child, 
52                    gchar *repo_id)
53 {
54   if (!strcasecmp(repo_id, "action")) return atk_is_action(child);
55   if (!strcasecmp(repo_id, "component")) return atk_is_component(child);
56   if (!strcasecmp(repo_id, "editabletext")) return atk_is_editable_text(child);
57   if (!strcasecmp(repo_id, "text")) return atk_is_text(child);
58   if (!strcasecmp(repo_id, "hypertext")) return atk_is_hypertext(child);
59   if (!strcasecmp(repo_id, "image")) return atk_is_image(child);
60   if (!strcasecmp(repo_id, "selection")) return atk_is_selection(child);
61   if (!strcasecmp(repo_id, "table")) return atk_is_table(child);
62   if (!strcasecmp(repo_id, "value")) return atk_is_value(child);
63   if (!strcasecmp(repo_id, "streamablecontent")) return atk_is_streamable_content(child);
64   if (!strcasecmp(repo_id, "document")) return atk_is_document(child);
65   return FALSE;
66 }
67
68 #define child_collection_p(ch) (TRUE)
69
70 static gboolean
71 match_states_all_p (AtkObject *child, 
72                     gint *set)
73 {
74      AtkStateSet *chs;
75      gint i;
76      gboolean ret = TRUE;
77
78      if (set == NULL)
79           return TRUE;
80
81      chs = atk_object_ref_state_set (child);
82
83      // TODO: use atk-state_set_contains_states; would be more efficient
84      for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
85      {
86           if (!atk_state_set_contains_state(chs, set[i]))
87           {
88                ret = FALSE;
89                break;
90           }
91      }
92    
93      g_object_unref(chs);
94      return ret;
95 }
96
97 static gboolean
98 match_states_any_p  (AtkObject *child, 
99                      gint *set)
100 {
101      AtkStateSet *chs;
102      gint i;
103      gboolean ret = FALSE;
104
105      if (set == NULL)
106           return TRUE;
107
108      chs = atk_object_ref_state_set (child);
109
110      for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
111      {
112           if (!atk_state_set_contains_state(chs, set[i]))
113           {
114                ret = TRUE;
115                break;
116           }
117      }
118    
119      g_object_unref(chs);
120      return ret;
121 }
122
123 static gboolean
124 match_states_none_p  (AtkObject *child, 
125                      gint *set)
126 {
127      AtkStateSet *chs;
128      gint i;
129      gboolean ret = TRUE;
130
131      if (set == NULL)
132           return TRUE;
133
134      chs = atk_object_ref_state_set (child);
135
136      for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
137      {
138           if (atk_state_set_contains_state(chs, set[i]))
139           {
140                ret = FALSE;
141                break;
142           }
143      }
144    
145      g_object_unref(chs);
146      return ret;
147 }
148
149 // TODO: need to convert at-spi roles/states to atk roles/states */
150 static gboolean
151 match_states_lookup (AtkObject *child,  
152                      MatchRulePrivate *mrp)
153 {
154      switch (mrp->statematchtype){
155      case Accessibility_Collection_MATCH_ALL : 
156           if (match_states_all_p (child, mrp->states))
157                return TRUE;
158           break;
159           
160      case  Accessibility_Collection_MATCH_ANY :
161           if (match_states_any_p (child, mrp->states))
162                return TRUE;
163           break;
164           
165      case  Accessibility_Collection_MATCH_NONE :
166           if (match_states_none_p (child, mrp->states))
167                return TRUE;
168           break;
169
170       default : break;    
171      }
172
173      return FALSE;    
174 }
175
176 // TODO: Map at-spi -> atk roles at mrp creation instead of doing this;
177 // would be more efficient
178 #define spi_get_role(obj) spi_accessible_role_from_atk_role(atk_object_get_role(obj))
179
180 static gboolean
181 match_roles_all_p (AtkObject *child, 
182                    gint *roles)
183 {
184    Accessibility_Role role; 
185
186      if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) return TRUE;
187      else if (roles[1] != BITARRAY_SEQ_TERM) return FALSE;
188
189      return (atk_object_get_role(child) == roles[0]);
190     
191 }
192
193 static gboolean
194 match_roles_any_p (AtkObject *child, 
195                    gint *roles)
196 {
197      AtkRole role;
198      int i;
199
200      if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) return TRUE;
201
202      role = atk_object_get_role(child);
203
204      for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
205           if (role  == roles[i])
206                return TRUE;
207
208      return FALSE;
209 }
210
211 static gboolean
212 match_roles_none_p (AtkObject *child, 
213                     gint *roles)
214 {
215   AtkRole role;
216      int i;
217
218      if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) return TRUE;
219
220      role = atk_object_get_role(child);
221
222      for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
223           if (role == roles[i])
224                return FALSE;
225
226      return TRUE;
227 }
228
229 static gboolean
230 match_roles_lookup (AtkObject *child,  
231                     MatchRulePrivate *mrp)
232 {
233       switch (mrp->rolematchtype){
234          case Accessibility_Collection_MATCH_ALL : 
235               if (match_roles_all_p (child, mrp->roles))
236                    return TRUE;
237               break;
238
239          case  Accessibility_Collection_MATCH_ANY :
240               if (match_roles_any_p (child, mrp->roles))
241                    return TRUE;
242               break;
243
244          case  Accessibility_Collection_MATCH_NONE :
245               if (match_roles_none_p (child, mrp->roles))
246                    return TRUE;
247               break;
248
249       default : break;
250  
251          }
252       return FALSE;
253 }
254
255 static gboolean
256 match_interfaces_all_p (AtkObject *obj, 
257                         gchar **ifaces)
258 {
259      gint i;
260
261      if (ifaces == NULL)
262        return TRUE;
263
264      for (i = 0; ifaces[i]; i++)
265        if (!child_interface_p (obj, ifaces [i])){
266                return FALSE;
267        }
268      return TRUE;
269 }
270
271 static gboolean
272 match_interfaces_any_p (AtkObject *obj, 
273                         gchar **ifaces)
274 {
275      gint i;
276
277      if (ifaces == NULL)
278        return TRUE;
279
280
281      for (i = 0; ifaces[i]; i++)
282        if (child_interface_p (obj, ifaces [i])){
283                 return TRUE;
284        }
285      return FALSE;
286 }
287
288 static gboolean
289 match_interfaces_none_p (AtkObject *obj, 
290                          gchar **ifaces)
291 {
292      gint i;
293
294      for (i = 0; ifaces[i]; i++)
295            if (child_interface_p (obj, ifaces [i]))
296                 return FALSE;
297      
298      return TRUE;
299 }
300
301 static gboolean
302 match_interfaces_lookup (AtkObject *child, 
303                          MatchRulePrivate *mrp)
304 {
305      switch (mrp->interfacematchtype){
306
307      case Accessibility_Collection_MATCH_ALL : 
308           if (match_interfaces_all_p (child, mrp->ifaces))
309                return TRUE;
310           break;
311
312      case  Accessibility_Collection_MATCH_ANY :
313           if (match_interfaces_any_p (child, mrp->ifaces))
314                return TRUE;
315           break;
316
317      case  Accessibility_Collection_MATCH_NONE :
318           if (match_interfaces_none_p (child, mrp->ifaces))
319                return TRUE;
320           break;
321
322       default : break;    
323      }
324
325      return FALSE;     
326 }
327
328 #define split_attributes(attributes) (g_strsplit (attributes, ";", 0))
329
330 static gboolean 
331 match_attributes_all_p (AtkObject *child, 
332                         AtkAttributeSet *attributes)
333 {
334      int i, k;
335      AtkAttributeSet *oa;
336      gint length, oa_length;
337      gboolean flag = FALSE;
338
339      if (attributes == NULL || g_slist_length (attributes) == 0)
340           return TRUE;
341      
342      oa =  atk_object_get_attributes(child);
343      length = g_slist_length(attributes);
344      oa_length = g_slist_length(oa);
345
346      for (i = 0; i < length; i++) {
347           AtkAttribute *attr = g_slist_nth_data(attributes, i);
348           for (k = 0; k < oa_length; k++) {
349                AtkAttribute *oa_attr = g_slist_nth_data(attributes, i);
350                if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
351                    !g_ascii_strcasecmp (oa_attr->value, attr->value)){
352                     flag = TRUE;
353                     break;
354                }
355                else
356                     flag = FALSE;
357                }
358           if (!flag) {
359                atk_attribute_set_free(oa);
360                return FALSE; 
361           }
362      }
363      atk_attribute_set_free(oa);
364      return TRUE;
365 }
366
367 static gboolean 
368 match_attributes_any_p (AtkObject *child, 
369                         AtkAttributeSet *attributes)
370 {
371      int i, k;
372
373      AtkAttributeSet *oa;
374      gint length, oa_length;
375
376      length = g_slist_length(attributes);
377      if (length == 0)
378           return TRUE;
379
380      oa = atk_object_get_attributes(child);
381      oa_length = g_slist_length(oa);
382
383      for (i = 0; i < length; i++){
384           AtkAttribute *attr = g_slist_nth_data(attributes, i);
385           for (k = 0; k < oa_length; k++){
386                AtkAttribute *oa_attr = g_slist_nth_data(attributes, i);
387                if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
388                    !g_ascii_strcasecmp (oa_attr->value, attr->value)){
389                     atk_attribute_set_free(oa);
390                     return TRUE;
391                     }
392                }
393           }
394      atk_attribute_set_free(oa);
395      return FALSE;
396 }
397
398 static gboolean 
399 match_attributes_none_p (AtkObject *child, 
400                          AtkAttributeSet *attributes)
401 {
402      int i, k;
403
404      AtkAttributeSet *oa;
405      gint length, oa_length;
406
407      length = g_slist_length(attributes);
408      if (length == 0)
409           return TRUE;
410
411      oa = atk_object_get_attributes(child);
412      oa_length = g_slist_length(oa);
413
414      for (i = 0; i < length; i++){
415           AtkAttribute *attr = g_slist_nth_data(attributes, i);
416           for (k = 0; k < oa_length; k++){
417                AtkAttribute *oa_attr = g_slist_nth_data(attributes, i);
418                if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
419                    !g_ascii_strcasecmp (oa_attr->value, attr->value)){
420                     atk_attribute_set_free(oa);
421                     return FALSE;
422                     }
423                }
424           }
425      atk_attribute_set_free(oa);
426      return TRUE;
427 }
428
429 static gboolean
430 match_attributes_lookup (AtkObject *child, MatchRulePrivate *mrp)
431 {
432      switch (mrp->attributematchtype){
433
434           case Accessibility_Collection_MATCH_ALL : 
435           if (match_attributes_all_p (child, mrp->attributes))
436                return TRUE;
437           break;
438           
439      case  Accessibility_Collection_MATCH_ANY :
440           if (match_attributes_any_p (child, mrp->attributes))
441                return TRUE;
442           break;
443           
444      case  Accessibility_Collection_MATCH_NONE :
445           if (match_attributes_none_p (child, mrp->attributes))
446                return TRUE;
447           break;
448
449       default : break;    
450      }
451      return FALSE;   
452 }
453
454 static gboolean
455 traverse_p (AtkObject *child, 
456             const gboolean traverse)
457 {
458   if (traverse)
459     return TRUE;
460   else return !child_collection_p (child);
461 }
462
463 static int 
464 sort_order_canonical (MatchRulePrivate *mrp, GList *ls,                       
465                       gint kount, gint max,
466                       AtkObject *obj, glong index, gboolean flag,
467                       AtkObject *pobj, gboolean recurse, 
468                       gboolean traverse)
469 {
470      gint i = index;
471      glong acount  = atk_object_get_n_accessible_children (obj);
472      gboolean prev = pobj? TRUE : FALSE;
473      
474      for (; i < acount && (max == 0 || kount < max); i++){
475           AtkObject *child = 
476                         atk_object_ref_accessible_child(obj, i);
477
478           g_object_unref(child);
479           if (prev && child == pobj){
480             return kount;           
481           }
482          
483           if (flag  && match_interfaces_lookup (child, mrp)
484                     && match_states_lookup (child, mrp)
485                     && match_roles_lookup (child, mrp)
486                     && match_attributes_lookup (child, mrp)
487                     ){
488            
489             ls = g_list_append (ls, child);
490             kount++;
491           }
492
493           if (!flag)
494                flag = TRUE;
495
496           if (recurse && traverse_p (child, traverse))
497             kount = sort_order_canonical (mrp, ls,  kount, 
498                                           max, child, 0, TRUE, 
499                                           pobj, recurse, traverse);
500      }
501      return kount;
502
503
504 static int 
505 sort_order_rev_canonical (MatchRulePrivate *mrp, GList *ls,                   
506                       gint kount, gint max,
507                       AtkObject *obj, gboolean flag, 
508                       AtkObject *pobj)
509 {
510     AtkObject *nextobj;
511     AtkObject *parent;
512     glong indexinparent;
513
514     /* This breaks us out of the recursion. */
515     if (!obj || obj == pobj)
516     {
517         return kount;           
518     } 
519          
520     /* Add to the list if it matches */
521     if (flag && match_interfaces_lookup (obj, mrp) 
522                && match_states_lookup (obj, mrp)
523                && match_roles_lookup (obj, mrp)
524                && match_attributes_lookup (obj, mrp))
525     {
526          ls = g_list_append (ls, obj);
527          kount++;
528     }
529
530     if(!flag) flag = TRUE;
531
532     /* Get the current nodes index in it's parent and the parent object. */
533     indexinparent = atk_object_get_index_in_parent (obj);
534     parent = atk_object_get_parent(obj);
535
536     if(indexinparent > 0)
537     {
538          /* there are still some siblings to visit so get the previous sibling
539             and get it's last descendant.
540             First, get the previous sibling */
541          nextobj = atk_object_ref_accessible_child (parent, 
542                                                              indexinparent-1);
543          g_object_unref(nextobj);
544
545          /* Now, drill down the right side to the last descendant */
546          while(atk_object_get_n_accessible_children (nextobj) > 0)
547          {
548               nextobj = atk_object_ref_accessible_child(nextobj,
549                  atk_object_get_n_accessible_children (nextobj)-1);
550               g_object_unref (nextobj);
551          } 
552          /* recurse with the last descendant */
553          kount = sort_order_rev_canonical (mrp, ls,  kount, max, 
554                                        nextobj, TRUE, pobj);
555     } 
556     else
557     {
558          /* no more siblings so next node must be the parent */
559          kount = sort_order_rev_canonical (mrp, ls,  kount, max, 
560                                        parent, TRUE, pobj);
561
562     }
563     return kount;
564
565
566 static int
567 query_exec (MatchRulePrivate *mrp,  Accessibility_Collection_SortOrder sortby, 
568             GList *ls, gint kount, gint max, 
569             AtkObject *obj, glong index, 
570             gboolean flag, 
571             AtkObject *pobj,
572             gboolean recurse, gboolean traverse)
573 {
574      switch (sortby) {
575      case Accessibility_Collection_SORT_ORDER_CANONICAL :  
576        kount = sort_order_canonical(mrp, ls, 0, max, obj, index, flag, 
577                                     pobj, recurse, traverse); 
578        break;
579      case Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL :
580        kount = sort_order_canonical(mrp, ls, 0, max, obj, index, flag, 
581                                     pobj, recurse, traverse);    
582        break;
583      default: 
584        kount = 0; 
585        g_warning ("Sort method not implemented yet"); 
586        break; 
587      }
588      
589      return kount;
590 }
591
592 static dbus_bool_t
593 read_mr(DBusMessageIter *iter, MatchRulePrivate *mrp)
594 {
595   DBusMessageIter mrc, arrayc;
596   dbus_uint32_t *array;
597   dbus_int32_t matchType;
598   int array_count;
599   char *str;
600   AtkAttribute *attr;
601   int i;
602   char *interfaces = NULL;
603
604   // TODO: error checking
605   dbus_message_iter_recurse(iter, &mrc);
606   dbus_message_iter_recurse(&mrc, &arrayc);
607   dbus_message_iter_get_fixed_array(&arrayc, &array, &array_count);
608   bitarray_to_seq(array, array_count, &mrp->states);
609   for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++)
610   {
611     mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]);
612   }
613   dbus_message_iter_next(&mrc);
614   dbus_message_iter_read_basic(&mrc, &matchType);
615   dbus_message_iter_next(&mrc);
616   mrp->statematchtype = matchType;;
617   /* attributes */
618   dbus_message_iter_recurse(&mrc, &arrayc);
619   mrp->attributes = NULL;
620   while (dbus_message_iter_get_arg_type(&arrayc) != DBUS_TYPE_INVALID)
621   {
622     dbus_message_iter_get_basic(&arrayc, &str);
623     // TODO: remove this print
624     g_print("Got attribute: %s\n", str);
625     attr = g_new (AtkAttribute, 1);
626     if (attr)
627     {
628       int len = strcspn(str, ":");
629       attr->name = g_strndup(str, len);
630       if (str[len] == ':')
631       {
632         len++;
633         if (str[len] == ' ') len++;
634         attr->value = g_strdup(str + len);
635       }
636       else attr->value = NULL;
637       mrp->attributes = g_slist_prepend(mrp->attributes, attr);
638     }
639     dbus_message_iter_next(&arrayc);
640   }
641   dbus_message_iter_next(&mrc);
642   dbus_message_iter_read_basic(&mrc, &matchType);
643   mrp->attributematchtype = matchType;;
644   dbus_message_iter_next(&mrc);
645   /* Get roles and role match */
646   dbus_message_iter_recurse(&mrc, &arrayc);
647   dbus_message_iter_get_fixed_array(&arrayc, &array, &array_count);
648   bitarray_to_seq(array, array_count, &mrp->roles);
649   dbus_message_iter_next(&mrc);
650   dbus_message_iter_read_basic(&mrc, &matchType);
651   mrp->rolematchtype = matchType;;
652   dbus_message_iter_next(&mrc);
653   /* Get interfaces and interface match */
654   dbus_message_iter_read_basic(&mrc, &interfaces);
655   dbus_message_iter_next(&mrc);
656   mrp->ifaces = g_strsplit(interfaces, ";", 0);
657   dbus_message_iter_read_basic(&mrc, &matchType);
658   mrp->interfacematchtype = matchType;;
659   dbus_message_iter_next(&mrc);
660   /* get invert */
661   dbus_message_iter_read_basic(&mrc, &mrp->invert);
662   dbus_message_iter_next(iter);
663   return TRUE;
664 }
665
666 static DBusMessage *
667 return_and_free_list(DBusMessage *message, GList *ls)
668 {
669   DBusMessage *reply;
670   DBusMessageIter iter, iter_array;
671   GList *item;
672
673   reply = dbus_message_new_method_return(message);
674   if (!reply) return NULL;
675   dbus_message_iter_init_append(reply, &iter);
676   if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &iter_array)) goto oom;
677   for (item = ls; item; item = g_list_next(item))
678   {
679       char *path = (char *) spi_dbus_object_to_path ((AtkObject *)item->data);
680       dbus_message_iter_append_basic(&iter_array, DBUS_TYPE_OBJECT_PATH, &path);
681       g_free(path);
682   }
683   if (!dbus_message_iter_close_container(&iter, &iter_array)) goto oom;
684   g_list_free (ls);
685   return reply;
686 oom:
687   // TODO: Handle out of memory
688   g_list_free (ls);
689   return reply;
690 }
691
692 static void free_mrp_data(MatchRulePrivate *mrp)
693 {
694   g_free(mrp->states);
695   atk_attribute_set_free(mrp->attributes);
696   g_free(mrp->roles);
697   g_strfreev(mrp->ifaces);
698 }
699
700 static DBusMessage *
701 getMatchesFrom (DBusMessage *message,
702                      AtkObject *current_object,
703                      MatchRulePrivate *mrp,
704                      const Accessibility_Collection_SortOrder sortby,
705                      const dbus_bool_t isrestrict,
706                      dbus_int32_t  count,
707                      const dbus_bool_t traverse)
708 {
709      GList *ls = NULL;
710      AtkObject *parent;
711      glong index = 
712            atk_object_get_index_in_parent (current_object);
713      gint kount = 0;
714
715      ls = g_list_append (ls, current_object);
716           
717      if (!isrestrict)
718      {
719           parent = atk_object_get_parent (current_object);
720           kount = query_exec (mrp,  sortby, ls, 0, count, parent, index, 
721                               FALSE, NULL, TRUE, traverse);
722      }
723      else 
724           kount = query_exec (mrp,  sortby, ls, 0, count, 
725                               current_object, 0, FALSE, NULL, 
726                               TRUE, traverse);
727
728      ls = g_list_remove (ls, ls->data);
729
730      if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
731        ls = g_list_reverse (ls);
732  
733      free_mrp_data (mrp);
734      return return_and_free_list (message, ls);
735 }
736
737 /*
738   inorder traversal from a given object in the hierarchy
739 */
740
741 static int
742 inorder (AtkObject *collection, MatchRulePrivate *mrp, 
743         GList *ls, gint kount, gint max,
744         AtkObject *obj,
745         gboolean flag,
746         AtkObject *pobj,
747         dbus_bool_t traverse)
748 {
749   int i = 0;
750   
751   /* First, look through the children recursively. */
752   kount = sort_order_canonical (mrp, ls, kount, max, obj, 0, TRUE,
753                                 NULL, TRUE, TRUE); 
754   
755   /* Next, we look through the right subtree */
756   while ((max == 0 || kount < max) 
757           && obj != collection)
758   {
759     AtkObject *parent =
760                                 atk_object_get_parent (obj);
761     i = atk_object_get_index_in_parent (obj);
762     kount  = sort_order_canonical (mrp, ls, kount, max, parent, 
763                                    i+1, TRUE, FALSE, TRUE, TRUE);
764     obj = parent;
765   }
766
767   if (kount < max)
768   {
769      kount = sort_order_canonical (mrp, ls, kount, max, 
770                                    obj, i + 1, TRUE, FALSE, 
771                                    TRUE, TRUE);
772   }
773
774   return kount;
775 }
776
777 /*
778   GetMatchesInOrder: get matches from a given object in an inorder traversal.
779 */
780
781 static DBusMessage *
782 getMatchesInOrder (DBusMessage *message,
783                    AtkObject *current_object,
784                    MatchRulePrivate *mrp,
785                    const Accessibility_Collection_SortOrder sortby,
786                    const dbus_bool_t recurse,
787                    dbus_int32_t count,
788                    const dbus_bool_t traverse)
789 {
790   GList *ls = NULL;
791   AtkObject *obj;
792   gint kount = 0;
793
794   ls = g_list_append (ls, current_object);
795
796   obj = atk_dbus_path_to_object (dbus_message_get_path (message));
797   
798   kount = inorder (obj, mrp, ls, 0, count, 
799                    current_object, TRUE, NULL, traverse);
800
801   ls = g_list_remove (ls, ls->data);
802
803   if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
804     ls = g_list_reverse (ls);
805
806   free_mrp_data (mrp);
807   return return_and_free_list (message, ls);
808 }
809
810 /*
811   GetMatchesInBackOrder: get matches from a given object in a
812   reverse order traversal.
813 */
814
815 static DBusMessage *
816 getMatchesInBackOrder (DBusMessage *message,
817                    AtkObject *current_object,
818                    MatchRulePrivate *mrp,
819                    const Accessibility_Collection_SortOrder sortby,
820                    dbus_int32_t count)
821 {
822   GList *ls = NULL;
823   AtkObject *collection;
824   gint kount = 0;
825
826   ls = g_list_append (ls, current_object);
827
828   collection = atk_dbus_path_to_object (dbus_message_get_path (message));
829
830   kount = sort_order_rev_canonical (mrp, ls, 0, count, current_object, 
831                                    FALSE, collection);
832
833   ls = g_list_remove (ls, ls->data);
834
835   if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
836     ls = g_list_reverse (ls);
837
838   free_mrp_data (mrp);
839   return return_and_free_list (message, ls);
840 }
841
842 static DBusMessage *
843 getMatchesTo (DBusMessage *message,
844               AtkObject *current_object,
845               MatchRulePrivate *mrp,
846               const Accessibility_Collection_SortOrder sortby,
847               const dbus_bool_t recurse, 
848               const dbus_bool_t isrestrict,
849               dbus_int32_t  count,
850               const dbus_bool_t traverse)
851 {
852   GList *ls = NULL;
853   AtkObject *obj;
854   gint kount = 0;
855
856   ls = g_list_append (ls, current_object); 
857     
858   if (recurse){
859     obj = atk_object_get_parent (current_object);
860     kount =  query_exec (mrp,  sortby, ls, 0, count, 
861                          obj, 0, TRUE, current_object, TRUE, traverse);
862   }
863   else{ 
864     obj = atk_dbus_path_to_object (dbus_message_get_path (message));
865     kount = query_exec (mrp,  sortby, ls, 0, count,
866                         obj, 0, TRUE, current_object, TRUE, traverse); 
867
868   }
869
870   ls = g_list_remove (ls, ls->data);
871    
872   if (sortby != Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
873     ls = g_list_reverse (ls);
874
875   free_mrp_data (mrp);
876   return return_and_free_list (message, ls);
877 }
878
879 static DBusMessage *
880 impl_getMatchesFrom (DBusConnection *bus, DBusMessage *message, void *user_data)
881 {
882   char *current_object_path = NULL;
883   AtkObject *current_object;
884   DBusMessageIter iter;
885   MatchRulePrivate rule;
886   dbus_uint16_t sortby;
887   dbus_uint16_t tree;
888   dbus_int32_t count;
889   dbus_bool_t traverse;
890   GList *ls = NULL;
891
892   dbus_message_iter_init(message, &iter);
893   dbus_message_iter_get_basic (&iter, current_object_path);
894   current_object = atk_dbus_path_to_object (current_object_path);
895   if (!current_object)
896   {
897     // TODO: object-not-found error
898     return spi_dbus_general_error (message);
899   }
900   dbus_message_iter_next (&iter);
901   if (!read_mr(&iter, &rule))
902   {
903     return spi_dbus_general_error (message);
904   }
905   dbus_message_iter_get_basic(&iter, &sortby);
906   dbus_message_iter_next(&iter);
907   dbus_message_iter_get_basic(&iter, &tree);
908   dbus_message_iter_next(&iter);
909   dbus_message_iter_get_basic(&iter, &count);
910   dbus_message_iter_next(&iter);
911   dbus_message_iter_get_basic(&iter, &traverse);
912   dbus_message_iter_next(&iter);
913
914   switch (tree){
915   case Accessibility_Collection_TREE_RESTRICT_CHILDREN : 
916     return getMatchesFrom (message, current_object, 
917                            &rule, sortby, TRUE, count, traverse);
918     break;
919   case Accessibility_Collection_TREE_RESTRICT_SIBLING :
920     return getMatchesFrom (message, current_object, 
921                            &rule, sortby, FALSE, count, traverse); 
922     break;
923   case Accessibility_Collection_TREE_INORDER :
924     return getMatchesInOrder (message, current_object, 
925                               &rule, sortby, TRUE, count, traverse); 
926     break;
927   default : return NULL;
928   }
929 }
930
931 static DBusMessage *
932 impl_getMatchesTo (DBusConnection *bus, DBusMessage *message, void *user_data)
933 {
934   char *current_object_path = NULL;
935   AtkObject *current_object;
936   DBusMessageIter iter;
937   MatchRulePrivate rule;
938   dbus_uint16_t sortby;
939   dbus_uint16_t tree;
940   dbus_bool_t recurse;
941   dbus_int32_t count;
942   dbus_bool_t traverse;
943   GList *ls = NULL;
944
945   dbus_message_iter_init(message, &iter);
946   dbus_message_iter_get_basic (&iter, current_object_path);
947   current_object = atk_dbus_path_to_object (current_object_path);
948   if (!current_object)
949   {
950     // TODO: object-not-found error
951     return spi_dbus_general_error (message);
952   }
953   dbus_message_iter_next (&iter);
954   if (!read_mr(&iter, &rule))
955   {
956     return spi_dbus_general_error (message);
957   }
958   dbus_message_iter_get_basic(&iter, &sortby);
959   dbus_message_iter_next(&iter);
960   dbus_message_iter_get_basic(&iter, &tree);
961   dbus_message_iter_next(&iter);
962   dbus_message_iter_get_basic(&iter, &recurse);
963   dbus_message_iter_next(&iter);
964   dbus_message_iter_get_basic(&iter, &count);
965   dbus_message_iter_next(&iter);
966   dbus_message_iter_get_basic(&iter, &traverse);
967   dbus_message_iter_next(&iter);
968
969   switch (tree){
970   case Accessibility_Collection_TREE_RESTRICT_CHILDREN : 
971     return getMatchesTo (message, current_object, 
972                          &rule, sortby, recurse, TRUE, count, traverse); 
973     break;
974   case Accessibility_Collection_TREE_RESTRICT_SIBLING :
975     return getMatchesTo (message, current_object, 
976                          &rule, sortby, recurse, FALSE, count, traverse); 
977     break;
978   case Accessibility_Collection_TREE_INORDER :
979     return getMatchesInBackOrder (message, current_object, 
980                                   &rule, sortby, count); 
981     break;
982   default : return NULL;
983   }
984 }
985
986 static DBusMessage *
987 impl_getMatches(DBusConnection *bus, DBusMessage *message, void *user_data)
988 {
989   AtkObject *obj = atk_dbus_path_to_object (dbus_message_get_path (message));
990   DBusMessageIter iter;
991   MatchRulePrivate rule;
992   dbus_uint16_t sortby;
993   dbus_int32_t count;
994   dbus_bool_t traverse;
995   GList *ls = NULL;
996
997   dbus_message_iter_init(message, &iter);
998   if (!read_mr(&iter, &rule))
999   {
1000     return spi_dbus_general_error (message);
1001   }
1002   dbus_message_iter_get_basic(&iter, &sortby);
1003   dbus_message_iter_next(&iter);
1004   dbus_message_iter_get_basic(&iter, &count);
1005   dbus_message_iter_next(&iter);
1006   dbus_message_iter_get_basic(&iter, &traverse);
1007   dbus_message_iter_next(&iter);
1008      ls = g_list_prepend (ls, obj);
1009   count = query_exec (&rule, sortby, ls, 0, count,
1010                          obj, 0, TRUE, NULL, TRUE, traverse);
1011      ls = g_list_remove (ls, ls->data);
1012       
1013      if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
1014        ls = g_list_reverse (ls);
1015      free_mrp_data (&rule);
1016      return return_and_free_list (message, ls);
1017 }
1018
1019 static DRouteMethod methods[] = {
1020   { impl_getMatchesFrom, "getMatchesFrom" },
1021   { impl_getMatchesTo, "getMatchesTo" },
1022   { impl_getMatches, "getMatches" },
1023   {NULL, NULL}
1024 };
1025
1026 void
1027 spi_initialize_collection (DRoutePath *path)
1028 {
1029   droute_path_add_interface (path,
1030                              SPI_DBUS_INTERFACE_COLLECTION,
1031                              methods,
1032                              NULL);
1033 };