2008-07-29 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/upstream/at-spi2-core.git] / pyatspi / cache.py
1 #Copyright (C) 2008 Codethink Ltd
2
3 #This library is free software; you can redistribute it and/or
4 #modify it under the terms of the GNU Lesser General Public
5 #License version 2 as published by the Free Software Foundation.
6
7 #This program is distributed in the hope that it will be useful,
8 #but WITHOUT ANY WARRANTY; without even the implied warranty of
9 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 #GNU General Public License for more details.
11 #You should have received a copy of the GNU Lesser General Public License
12 #along with this program; if not, write to the Free Software
13 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
15 from base import AccessibleObjectNoLongerExists
16 from factory import interfaceFactory
17
18 #------------------------------------------------------------------------------
19
20 class _CacheData(object):
21         __slots__ =     [
22                         'parent',
23                         'interfaces',
24                         'children',
25                         'role',
26                         'name',
27                         'description',
28                         ]
29
30         def __init__(self, data):
31                 self.update(data)
32
33         def update(self, data):
34                 #Don't cache the path here, used as lookup in cache object dict.
35                 (path,
36                 self.parent,
37                 self.children,
38                 self.interfaces,
39                 self.name,
40                 self.role,
41                 self.description) = data
42
43 #------------------------------------------------------------------------------
44
45 class BaseCache(object):
46         """
47         Base object for the Desktop, Accessible and Application caches.
48
49         Abstracts common initialization.
50         """
51
52
53         def __init__(self, connection, bus_name):
54                 """
55                 Creates a cache.
56
57                 connection - DBus connection.
58                 busName    - Name of DBus connection where cache interface resides.
59                 """
60                 self._connection = connection
61                 self._bus_name = bus_name
62
63                 obj = connection.get_object(bus_name, self._PATH)
64                 itf = dbus.Interface(obj, self._INTERFACE)
65
66                 self._objects = {}
67
68                 getMethod = itf.get_dbus_method(self._GET_METHOD)
69                 self._updateObjects(getMethod)
70
71                 self._signalMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._updateHandler)
72
73         def _updateHandler(self, updates):
74                 update, remove = updates
75                 self._removeObjects(update)
76                 self._updateObjects(remove)
77
78         def _updateObjects(self, objects):
79                 for data in objects:
80                         #First element is the object path.
81                         path = data[0]
82                         if path in self._objects:
83                                 cachedata = self._objects[path]
84                                 cachedata.update(data)
85                         else:
86                                 self._objects[path] = _CacheData(data)
87
88         def _removeObjects(self, paths):
89                 for path in paths:
90                         del(self._objects[path])
91
92
93         def getAccessible(self, path, interface, dbus_object=None):
94                 """
95                 Gets a client side proxy for the accessible object found
96                 at the path.
97
98                 path - The D-Bus path of the remote object.
99                 interface - The interface that the accessible object should support.
100                 dbus_object=None - The D-Bus proxy object backing this interface.
101                 """
102                 if path in self._objects:
103                         if not dbus_object:
104                                 dbus_object = self._connection.get_object(self._bus_name, path, introspect=False)
105                         return interfaceFactory(proxy, self, path, interface)
106                 else:
107                         raise AccessibleObjectNoLongerExists, "D-Bus reference not found in cache"
108
109 #------------------------------------------------------------------------------
110
111 class AccessibleCache(BaseCache):
112         """
113         There is one accessible cache per application.
114         For each application the accessible cache stores
115         data on every accessible object within the app.
116
117         It also acts as the factory for creating client
118         side proxies for these accessible objects.
119         """
120
121         _PATH = '/org/freedesktop/atspi/tree'
122         _INTERFACE = 'org.freedesktop.atspi.Tree'
123         _GET_METHOD = 'getTree'
124         _UPDATE_SIGNAL = 'updateTree'
125
126         def __init__(self, connection, bus_name):
127                 BaseCache.__init__(self, connection, bus_name)
128
129                 obj = connection.get_object(_self.bus_name, self._PATH)
130                 itf = dbus.Interface(obj, self._INTERFACE)
131
132                 self._root = itf.getRoot()
133
134         def getRootAccessible(self):
135                 """
136                 Gets the accessible object at the root of the tree.
137                 """
138                 return self.getAccessible(self._root)
139
140 #------------------------------------------------------------------------------
141
142 class DesktopCache(BaseCache):
143         """
144         Cache of desktop objects obtained from the registry.
145
146         The desktop interface on the registry object is the
147         same as that of the general tree interface on the
148         applications.
149
150         The difference is that the children data refers to
151         bus names of the applications rather than the object
152         paths of particular accessible objects within an application.
153         """
154
155         _PATH = '/org/freedesktop/atspi/registry'
156         _INTERFACE = 'org.freedesktop.atspi.Registry'
157         _GET_METHOD = 'getDesktops'
158         _UPDATE_SIGNAL = 'updateDesktops'
159         
160         def __init__(self, connection, bus_name):
161                 self._app_cache = ApplicationCache(connection, bus_name)
162
163         def getApplication(self, name):
164                 try:
165                         self._app_cache[name].getRootAccessible()
166                 except KeyError:
167                         raise AccessibleObjectNoLongerExists("Application no longer exists")
168
169 #------------------------------------------------------------------------------
170
171 class ApplicationCache(object):
172         """
173         Holds a mapping of bus names of each accessible application
174         to the applications accessible cache.
175
176         Makes calls and recieves updates from the registry
177         daemon to keep the cache up to date.
178         """
179
180         _PATH = '/org/freedesktop/atspi/registry'
181         _INTERFACE = 'org.freedesktop.atspi.Registry'
182         _GET_METHOD = 'getApplications'
183         _UPDATE_SIGNAL = 'updateApplications'
184
185         def _updateApplications(self, names):
186                 for name in names:
187                         if name not in self._applications:
188                                 self._applications[name] = AccessibleCache(self._connection,
189                                                                         self._busName,
190                                                                         self._treePath)
191
192         def _removeApplications(self, names):
193                 for name in names:
194                         del(self._applications[name])
195
196 #END---------------------------------------------------------------------------