Bump to 0.8.13
[theonering] / src / tp / channelmanager.py
1 # telepathy-python - Base classes defining the interfaces of the Telepathy framework
2 #
3 # Copyright (C) 2009-2010 Collabora Limited
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19 import warnings
20
21 from telepathy.errors import NotImplemented
22
23 from telepathy.interfaces import (CHANNEL_INTERFACE,
24                                   CHANNEL_TYPE_CONTACT_LIST)
25
26 from telepathy.constants import HANDLE_TYPE_NONE
27
28 from handle import NoneHandle
29
30 class ChannelManager(object):
31     def __init__(self, connection):
32         self._conn = connection
33
34         self._requestable_channels = dict()
35         self._channels = dict()
36
37         self._fixed_properties = dict()
38         self._available_properties = dict()
39
40         self._requestables = dict()
41
42     def close(self):
43         """Close channel manager and all the existing channels."""
44         for channel_type in self._requestable_channels:
45             for channels in self._channels[channel_type].values():
46                 for channel in channels:
47                     if channel._type == CHANNEL_TYPE_CONTACT_LIST:
48                         channel.remove_from_connection()
49                     else:
50                         channel.Close()
51
52     def remove_channel(self, channel):
53         "Remove channel from the channel manager"
54         for channel_type in self._requestable_channels:
55             for handle, channels in self._channels[channel_type].items():
56                 if channel in channels :
57                     channels.remove(channel)
58
59     def _get_type_requested_handle(self, props):
60         """Return the type, request and target handle from the requested
61         properties"""
62         type = props[CHANNEL_INTERFACE + '.ChannelType']
63         requested = props[CHANNEL_INTERFACE + '.Requested']
64
65         target_handle_type = \
66             props.get(CHANNEL_INTERFACE + '.TargetHandleType', HANDLE_TYPE_NONE)
67
68         if target_handle_type == HANDLE_TYPE_NONE:
69             handle = NoneHandle()
70         else:
71             target_handle = props[CHANNEL_INTERFACE + '.TargetHandle']
72             handle = self._conn._handles[target_handle_type, target_handle]
73
74         return (type, requested, handle)
75
76     def existing_channel(self, props):
77         """ Return a channel corresponding to theses properties if such
78         one exists, otherwhise return None. Default implementation will
79         return the last created channel of the same kind identified by
80         handle and type.
81         Connection Manager should subclass this function
82         to implement more appropriate behaviour. """
83
84         type, _, handle = self._get_type_requested_handle(props)
85
86         if type in self._channels:
87             if handle in self._channels[type]:
88                 if len(self._channels[type][handle]) > 0:
89                     return self._channels[type][handle][-1]
90
91         return None
92
93     def channel_exists(self, props):
94         """ Return True if channel exist with theses props, False otherwhise"""
95         return self.existing_channel(props) != None
96
97     def create_channel_for_props(self, props, signal=True, **args):
98         """Create a new channel with theses properties"""
99         type, _, handle = self._get_type_requested_handle(props)
100
101         if type not in self._requestable_channels:
102             raise NotImplemented('Unknown channel type "%s"' % type)
103
104         channel = self._requestable_channels[type](
105             props, **args)
106
107         self._conn.add_channels([channel], signal=signal)
108         if handle.get_type() != HANDLE_TYPE_NONE and type in self._channels:
109             self._channels[type].setdefault(handle, []).append(channel)
110
111         return channel
112
113     def channel_for_props(self, props, signal=True, **args):
114         channel = self.existing_channel(props)
115         """Return an existing channel with theses properties if it already
116         exists, otherwhise return a new one"""
117         if channel:
118             return channel
119         else:
120             return self.create_channel_for_props(props, signal, **args)
121
122     # Should use implement_channel_classes instead.
123     def _implement_channel_class(self, type, make_channel, fixed, available):
124         """Implement channel types in the channel manager, and add one channel
125         class that is retrieved in RequestableChannelClasses.
126
127         self.implement_channel_classes should be used instead, as it allows
128         implementing multiple channel classes."""
129         warnings.warn('deprecated in favour of implement_channel_classes',
130             DeprecationWarning)
131
132         self._requestable_channels[type] = make_channel
133         self._channels.setdefault(type, {})
134
135         self._fixed_properties[type] = fixed
136         self._available_properties[type] = available
137
138     # Use this function instead of _implement_channel_class.
139     def implement_channel_classes(self, type, make_channel, classes):
140         """Implement channel types in the channel manager, and add channel
141         classes that are retrieved in RequestableChannelClasses.
142
143           @type: the channel type
144           @make_channel: a function to call which returns a Channel object
145           @classes: a list of channel classes. E.g.
146
147             [ ( { '...ChannelType': '...Text', '...TargetHandleType': HANDLE_TYPE_CONTACT },
148                 ['...TargetHandle'] )
149             ]
150
151             See the spec for more documentation on the
152             Requestable_Channel_Class struct.
153         """
154         self._requestable_channels[type] = make_channel
155         self._channels.setdefault(type, {})
156
157         self._requestables[type] = classes
158
159     def get_requestable_channel_classes(self):
160         """Return all the channel types that can be created"""
161         retval = []
162
163         for channel_type in self._requestable_channels:
164             retval.extend(self._requestables.get(channel_type, []))
165
166             # _implement_channel_class was used.
167             if channel_type in self._fixed_properties:
168                 retval.append((self._fixed_properties[channel_type],
169                     self._available_properties.get(channel_type, [])))
170
171
172         return retval