1
##############################################################################
3
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
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.
13
##############################################################################
14
"""Ordered container implementation.
16
__docformat__ = 'restructuredtext'
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
27
@implementer(IOrderedContainer)
28
class OrderedContainer(Persistent, Contained):
29
""" `OrderedContainer` maintains entries' order as added and moved.
31
>>> oc = OrderedContainer()
32
>>> int(IOrderedContainer.providedBy(oc))
39
self._data = PersistentDict()
40
self._order = PersistentList()
43
""" See `IOrderedContainer`.
45
>>> oc = OrderedContainer()
51
>>> oc['baz'] = 'quux'
54
>>> int(len(oc._order) == len(oc._data))
61
""" See `IOrderedContainer`.
63
>>> oc = OrderedContainer()
67
>>> oc['baz'] = 'quux'
70
>>> int(len(oc._order) == len(oc._data))
74
return iter(self.keys())
76
def __getitem__(self, key):
77
""" See `IOrderedContainer`.
79
>>> oc = OrderedContainer()
85
return self._data[key]
87
def get(self, key, default=None):
88
""" See `IOrderedContainer`.
90
>>> oc = OrderedContainer()
94
>>> oc.get('funky', 'No chance, dude.')
98
return self._data.get(key, default)
101
""" See `IOrderedContainer`.
103
>>> oc = OrderedContainer()
106
>>> oc['foo'] = 'bar'
109
>>> oc['baz'] = 'quux'
112
>>> int(len(oc._order) == len(oc._data))
116
return [self._data[i] for i in self._order]
119
""" See `IOrderedContainer`.
121
>>> oc = OrderedContainer()
122
>>> int(len(oc) == 0)
124
>>> oc['foo'] = 'bar'
125
>>> int(len(oc) == 1)
129
return len(self._data)
132
""" See `IOrderedContainer`.
134
>>> oc = OrderedContainer()
137
>>> oc['foo'] = 'bar'
140
>>> oc['baz'] = 'quux'
142
[('foo', 'bar'), ('baz', 'quux')]
143
>>> int(len(oc._order) == len(oc._data))
147
return [(i, self._data[i]) for i in self._order]
149
def __contains__(self, key):
150
""" See `IOrderedContainer`.
152
>>> oc = OrderedContainer()
153
>>> oc['foo'] = 'bar'
156
>>> int('quux' in oc)
160
return self._data.has_key(key)
162
has_key = __contains__
164
def __setitem__(self, key, object):
165
""" See `IOrderedContainer`.
167
>>> oc = OrderedContainer()
170
>>> oc['foo'] = 'bar'
173
>>> oc['baz'] = 'quux'
176
>>> int(len(oc._order) == len(oc._data))
179
>>> oc['foo'] = 'baz'
180
Traceback (most recent call last):
187
existed = self._data.has_key(key)
190
if isinstance(key, StringTypes):
198
raise TypeError("'%s' is invalid, the key must be an "
199
"ascii or unicode string" % key)
201
raise ValueError("The key cannot be an empty string")
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.
207
self._order.append(key)
209
# This function creates a lot of events that other code listens to.
211
setitem(self, self._data.__setitem__, key, object)
214
self._order.remove(key)
219
def __delitem__(self, key):
220
""" See `IOrderedContainer`.
222
>>> oc = OrderedContainer()
225
>>> oc['foo'] = 'bar'
226
>>> oc['baz'] = 'quux'
227
>>> oc['zork'] = 'grue'
229
[('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')]
230
>>> int(len(oc._order) == len(oc._data))
234
[('foo', 'bar'), ('zork', 'grue')]
235
>>> int(len(oc._order) == len(oc._data))
239
uncontained(self._data[key], self, key)
241
self._order.remove(key)
243
def updateOrder(self, order):
244
""" See `IOrderedContainer`.
246
>>> oc = OrderedContainer()
247
>>> oc['foo'] = 'bar'
248
>>> oc['baz'] = 'quux'
249
>>> oc['zork'] = 'grue'
251
['foo', 'baz', 'zork']
252
>>> oc.updateOrder(['baz', 'foo', 'zork'])
254
['baz', 'foo', 'zork']
255
>>> oc.updateOrder(['baz', 'zork', 'foo'])
257
['baz', 'zork', 'foo']
258
>>> oc.updateOrder(['baz', 'zork', 'foo'])
260
['baz', 'zork', 'foo']
261
>>> oc.updateOrder(('zork', 'foo', 'baz'))
263
['zork', 'foo', 'baz']
264
>>> oc.updateOrder(['baz', 'zork'])
265
Traceback (most recent call last):
267
ValueError: Incompatible key set.
268
>>> oc.updateOrder(['foo', 'bar', 'baz', 'quux'])
269
Traceback (most recent call last):
271
ValueError: Incompatible key set.
272
>>> oc.updateOrder(1)
273
Traceback (most recent call last):
275
TypeError: order must be a tuple or a list.
276
>>> oc.updateOrder('bar')
277
Traceback (most recent call last):
279
TypeError: order must be a tuple or a list.
280
>>> oc.updateOrder(['baz', 'zork', 'quux'])
281
Traceback (most recent call last):
283
ValueError: Incompatible key set.
291
if not isinstance(order, ListType) and \
292
not isinstance(order, TupleType):
293
raise TypeError('order must be a tuple or a list.')
295
if len(order) != len(self._order):
296
raise ValueError("Incompatible key set.")
300
new_order = PersistentList()
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])
307
if will_be_dict != was_dict:
308
raise ValueError("Incompatible key set.")
310
self._order = new_order
311
notifyContainerModified(self)