~ztk-steering-group/zope.container/trunk

« back to all changes in this revision

Viewing changes to src/zope/container/ordered.py

  • Committer: alga
  • Date: 2013-02-14 14:20:19 UTC
  • Revision ID: svn-v4:62d5b8a3-27da-0310-9561-8e5933582275:zope.container/trunk:129409
Move to GitHub under https://github.com/zopefoundation/zope.container

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
##############################################################################
2
 
#
3
 
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4
 
# All Rights Reserved.
5
 
#
6
 
# This software is subject to the provisions of the Zope Public License,
7
 
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
 
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
 
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
 
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
 
# FOR A PARTICULAR PURPOSE.
12
 
#
13
 
##############################################################################
14
 
"""Ordered container implementation.
15
 
"""
16
 
__docformat__ = 'restructuredtext'
17
 
 
18
 
from zope.container.interfaces import IOrderedContainer
19
 
from zope.interface import implementer
20
 
from persistent import Persistent
21
 
from persistent.dict import PersistentDict
22
 
from persistent.list import PersistentList
23
 
from types import StringTypes, TupleType, ListType
24
 
from zope.container.contained import Contained, setitem, uncontained
25
 
from zope.container.contained import notifyContainerModified
26
 
 
27
 
@implementer(IOrderedContainer)
28
 
class OrderedContainer(Persistent, Contained):
29
 
    """ `OrderedContainer` maintains entries' order as added and moved.
30
 
 
31
 
    >>> oc = OrderedContainer()
32
 
    >>> int(IOrderedContainer.providedBy(oc))
33
 
    1
34
 
    >>> len(oc)
35
 
    0
36
 
    """
37
 
    def __init__(self):
38
 
 
39
 
        self._data = PersistentDict()
40
 
        self._order = PersistentList()
41
 
 
42
 
    def keys(self):
43
 
        """ See `IOrderedContainer`.
44
 
 
45
 
        >>> oc = OrderedContainer()
46
 
        >>> oc.keys()
47
 
        []
48
 
        >>> oc['foo'] = 'bar'
49
 
        >>> oc.keys()
50
 
        ['foo']
51
 
        >>> oc['baz'] = 'quux'
52
 
        >>> oc.keys()
53
 
        ['foo', 'baz']
54
 
        >>> int(len(oc._order) == len(oc._data))
55
 
        1
56
 
        """
57
 
 
58
 
        return self._order[:]
59
 
 
60
 
    def __iter__(self):
61
 
        """ See `IOrderedContainer`.
62
 
 
63
 
        >>> oc = OrderedContainer()
64
 
        >>> oc.keys()
65
 
        []
66
 
        >>> oc['foo'] = 'bar'
67
 
        >>> oc['baz'] = 'quux'
68
 
        >>> [i for i in oc]
69
 
        ['foo', 'baz']
70
 
        >>> int(len(oc._order) == len(oc._data))
71
 
        1
72
 
        """
73
 
 
74
 
        return iter(self.keys())
75
 
 
76
 
    def __getitem__(self, key):
77
 
        """ See `IOrderedContainer`.
78
 
 
79
 
        >>> oc = OrderedContainer()
80
 
        >>> oc['foo'] = 'bar'
81
 
        >>> oc['foo']
82
 
        'bar'
83
 
        """
84
 
 
85
 
        return self._data[key]
86
 
 
87
 
    def get(self, key, default=None):
88
 
        """ See `IOrderedContainer`.
89
 
 
90
 
        >>> oc = OrderedContainer()
91
 
        >>> oc['foo'] = 'bar'
92
 
        >>> oc.get('foo')
93
 
        'bar'
94
 
        >>> oc.get('funky', 'No chance, dude.')
95
 
        'No chance, dude.'
96
 
        """
97
 
 
98
 
        return self._data.get(key, default)
99
 
 
100
 
    def values(self):
101
 
        """ See `IOrderedContainer`.
102
 
 
103
 
        >>> oc = OrderedContainer()
104
 
        >>> oc.keys()
105
 
        []
106
 
        >>> oc['foo'] = 'bar'
107
 
        >>> oc.values()
108
 
        ['bar']
109
 
        >>> oc['baz'] = 'quux'
110
 
        >>> oc.values()
111
 
        ['bar', 'quux']
112
 
        >>> int(len(oc._order) == len(oc._data))
113
 
        1
114
 
        """
115
 
 
116
 
        return [self._data[i] for i in self._order]
117
 
 
118
 
    def __len__(self):
119
 
        """ See `IOrderedContainer`.
120
 
 
121
 
        >>> oc = OrderedContainer()
122
 
        >>> int(len(oc) == 0)
123
 
        1
124
 
        >>> oc['foo'] = 'bar'
125
 
        >>> int(len(oc) == 1)
126
 
        1
127
 
        """
128
 
 
129
 
        return len(self._data)
130
 
 
131
 
    def items(self):
132
 
        """ See `IOrderedContainer`.
133
 
 
134
 
        >>> oc = OrderedContainer()
135
 
        >>> oc.keys()
136
 
        []
137
 
        >>> oc['foo'] = 'bar'
138
 
        >>> oc.items()
139
 
        [('foo', 'bar')]
140
 
        >>> oc['baz'] = 'quux'
141
 
        >>> oc.items()
142
 
        [('foo', 'bar'), ('baz', 'quux')]
143
 
        >>> int(len(oc._order) == len(oc._data))
144
 
        1
145
 
        """
146
 
 
147
 
        return [(i, self._data[i]) for i in self._order]
148
 
 
149
 
    def __contains__(self, key):
150
 
        """ See `IOrderedContainer`.
151
 
 
152
 
        >>> oc = OrderedContainer()
153
 
        >>> oc['foo'] = 'bar'
154
 
        >>> int('foo' in oc)
155
 
        1
156
 
        >>> int('quux' in oc)
157
 
        0
158
 
        """
159
 
 
160
 
        return self._data.has_key(key)
161
 
 
162
 
    has_key = __contains__
163
 
 
164
 
    def __setitem__(self, key, object):
165
 
        """ See `IOrderedContainer`.
166
 
 
167
 
        >>> oc = OrderedContainer()
168
 
        >>> oc.keys()
169
 
        []
170
 
        >>> oc['foo'] = 'bar'
171
 
        >>> oc._order
172
 
        ['foo']
173
 
        >>> oc['baz'] = 'quux'
174
 
        >>> oc._order
175
 
        ['foo', 'baz']
176
 
        >>> int(len(oc._order) == len(oc._data))
177
 
        1
178
 
 
179
 
        >>> oc['foo'] = 'baz'
180
 
        Traceback (most recent call last):
181
 
        ...
182
 
        KeyError: u'foo'
183
 
        >>> oc._order
184
 
        ['foo', 'baz']
185
 
        """
186
 
 
187
 
        existed = self._data.has_key(key)
188
 
 
189
 
        bad = False
190
 
        if isinstance(key, StringTypes):
191
 
            try:
192
 
                unicode(key)
193
 
            except UnicodeError:
194
 
                bad = True
195
 
        else:
196
 
            bad = True
197
 
        if bad:
198
 
            raise TypeError("'%s' is invalid, the key must be an "
199
 
                            "ascii or unicode string" % key)
200
 
        if len(key) == 0:
201
 
            raise ValueError("The key cannot be an empty string")
202
 
 
203
 
        # We have to first update the order, so that the item is available,
204
 
        # otherwise most API functions will lie about their available values
205
 
        # when an event subscriber tries to do something with the container.
206
 
        if not existed:
207
 
            self._order.append(key)
208
 
 
209
 
        # This function creates a lot of events that other code listens to.
210
 
        try:
211
 
            setitem(self, self._data.__setitem__, key, object)
212
 
        except Exception:
213
 
            if not existed:
214
 
                self._order.remove(key)
215
 
            raise
216
 
 
217
 
        return key
218
 
 
219
 
    def __delitem__(self, key):
220
 
        """ See `IOrderedContainer`.
221
 
 
222
 
        >>> oc = OrderedContainer()
223
 
        >>> oc.keys()
224
 
        []
225
 
        >>> oc['foo'] = 'bar'
226
 
        >>> oc['baz'] = 'quux'
227
 
        >>> oc['zork'] = 'grue'
228
 
        >>> oc.items()
229
 
        [('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')]
230
 
        >>> int(len(oc._order) == len(oc._data))
231
 
        1
232
 
        >>> del oc['baz']
233
 
        >>> oc.items()
234
 
        [('foo', 'bar'), ('zork', 'grue')]
235
 
        >>> int(len(oc._order) == len(oc._data))
236
 
        1
237
 
        """
238
 
 
239
 
        uncontained(self._data[key], self, key)
240
 
        del self._data[key]
241
 
        self._order.remove(key)
242
 
 
243
 
    def updateOrder(self, order):
244
 
        """ See `IOrderedContainer`.
245
 
 
246
 
        >>> oc = OrderedContainer()
247
 
        >>> oc['foo'] = 'bar'
248
 
        >>> oc['baz'] = 'quux'
249
 
        >>> oc['zork'] = 'grue'
250
 
        >>> oc.keys()
251
 
        ['foo', 'baz', 'zork']
252
 
        >>> oc.updateOrder(['baz', 'foo', 'zork'])
253
 
        >>> oc.keys()
254
 
        ['baz', 'foo', 'zork']
255
 
        >>> oc.updateOrder(['baz', 'zork', 'foo'])
256
 
        >>> oc.keys()
257
 
        ['baz', 'zork', 'foo']
258
 
        >>> oc.updateOrder(['baz', 'zork', 'foo'])
259
 
        >>> oc.keys()
260
 
        ['baz', 'zork', 'foo']
261
 
        >>> oc.updateOrder(('zork', 'foo', 'baz'))
262
 
        >>> oc.keys()
263
 
        ['zork', 'foo', 'baz']
264
 
        >>> oc.updateOrder(['baz', 'zork'])
265
 
        Traceback (most recent call last):
266
 
        ...
267
 
        ValueError: Incompatible key set.
268
 
        >>> oc.updateOrder(['foo', 'bar', 'baz', 'quux'])
269
 
        Traceback (most recent call last):
270
 
        ...
271
 
        ValueError: Incompatible key set.
272
 
        >>> oc.updateOrder(1)
273
 
        Traceback (most recent call last):
274
 
        ...
275
 
        TypeError: order must be a tuple or a list.
276
 
        >>> oc.updateOrder('bar')
277
 
        Traceback (most recent call last):
278
 
        ...
279
 
        TypeError: order must be a tuple or a list.
280
 
        >>> oc.updateOrder(['baz', 'zork', 'quux'])
281
 
        Traceback (most recent call last):
282
 
        ...
283
 
        ValueError: Incompatible key set.
284
 
        >>> del oc['baz']
285
 
        >>> del oc['zork']
286
 
        >>> del oc['foo']
287
 
        >>> len(oc)
288
 
        0
289
 
        """
290
 
 
291
 
        if not isinstance(order, ListType) and \
292
 
            not isinstance(order, TupleType):
293
 
            raise TypeError('order must be a tuple or a list.')
294
 
 
295
 
        if len(order) != len(self._order):
296
 
            raise ValueError("Incompatible key set.")
297
 
 
298
 
        was_dict = {}
299
 
        will_be_dict = {}
300
 
        new_order = PersistentList()
301
 
 
302
 
        for i in range(len(order)):
303
 
            was_dict[self._order[i]] = 1
304
 
            will_be_dict[order[i]] = 1
305
 
            new_order.append(order[i])
306
 
 
307
 
        if will_be_dict != was_dict:
308
 
            raise ValueError("Incompatible key set.")
309
 
 
310
 
        self._order = new_order
311
 
        notifyContainerModified(self)