Package muntjac :: Package data :: Package util :: Module container_ordered_wrapper
[hide private]
[frames] | no frames]

Source Code for Module muntjac.data.util.container_ordered_wrapper

  1  # Copyright (C) 2012 Vaadin Ltd.  
  2  # Copyright (C) 2012 Richard Lincoln 
  3  #  
  4  # Licensed under the Apache License, Version 2.0 (the "License");  
  5  # you may not use this file except in compliance with the License.  
  6  # You may obtain a copy of the License at  
  7  #  
  8  #     http://www.apache.org/licenses/LICENSE-2.0  
  9  #  
 10  # Unless required by applicable law or agreed to in writing, software  
 11  # distributed under the License is distributed on an "AS IS" BASIS,  
 12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 13  # See the License for the specific language governing permissions and  
 14  # limitations under the License. 
 15   
 16  from muntjac.data.container \ 
 17      import IOrdered, IItemSetChangeNotifier, IPropertySetChangeNotifier,\ 
 18      IItemSetChangeListener, IPropertySetChangeListener, IItemSetChangeEvent,\ 
 19      IPropertySetChangeEvent 
 20   
 21   
22 -class ContainerOrderedWrapper(IOrdered, IItemSetChangeNotifier, 23 IPropertySetChangeNotifier):
24 """A wrapper class for adding external ordering to containers not 25 implementing the L{IOrdered} interface. 26 27 If the wrapped container is changed directly (that is, not through the 28 wrapper), and does not implement Container.ItemSetChangeNotifier and/or 29 PropertySetChangeNotifier the hierarchy information must be updated with 30 the L{updateOrderWrapper} method. 31 32 @author: Vaadin Ltd. 33 @author: Richard Lincoln 34 @version: 1.1.2 35 """ 36
37 - def __init__(self, toBeWrapped):
38 """Constructs a new ordered wrapper for an existing Container. Works even if 39 the to-be-wrapped container already implements the Container.Ordered 40 interface. 41 42 @param toBeWrapped 43 the container whose contents need to be ordered. 44 """ 45 #: The wrapped container 46 self._container = toBeWrapped 47 48 #: Ordering information, ie. the mapping from Item ID to the next 49 # item ID 50 self._next = None 51 52 #: Reverse ordering information for convenience and performance 53 # reasons. 54 self._prev = None 55 56 #: ID of the first Item in the container. 57 self._first = None 58 59 #: ID of the last Item in the container. 60 self._last = None 61 62 #: Is the wrapped container ordered by itself, ie. does it implement 63 # the IOrdered interface by itself? If it does, this class will use 64 # the methods of the underlying container directly. 65 self._ordered = False 66 67 #: The last known size of the wrapped container. Used to check whether 68 # items have been added or removed to the wrapped container, when the 69 # wrapped container does not send ItemSetChangeEvents. 70 self._lastKnownSize = -1 71 72 self._ordered = isinstance(self._container, IOrdered) 73 74 # Checks arguments 75 if self._container is None: 76 raise ValueError, 'Null can not be wrapped' 77 78 # Creates initial order if needed 79 self.updateOrderWrapper()
80 81
82 - def removeFromOrderWrapper(self, idd):
83 """Removes the specified Item from the wrapper's internal hierarchy 84 structure. 85 86 Note : The Item is not removed from the underlying Container. 87 88 @param idd: 89 the ID of the Item to be removed from the ordering. 90 """ 91 if idd is not None: 92 pid = self._prev.get(idd) 93 nid = self._next.get(idd) 94 if self._first == idd: 95 self._first = nid 96 if self._last == idd: 97 self._first = pid 98 if nid is not None: 99 self._prev[nid] = pid 100 if pid is not None: 101 self._next[pid] = nid 102 del self._next[idd] 103 del self._prev[idd]
104 105
106 - def addToOrderWrapper(self, idd, previousItemId=None):
107 """Registers the specified Item to the last position in the wrapper's 108 internal ordering. The underlying container is not modified. 109 110 @param idd 111 the ID of the Item to be added to the ordering. 112 --- 113 Registers the specified Item after the specified itemId in the wrapper's 114 internal ordering. The underlying container is not modified. Given item 115 idd must be in the container, or must be null. 116 117 @param idd 118 the ID of the Item to be added to the ordering. 119 @param previousItemId 120 the Id of the previous item. 121 """ 122 # Adds the if to tail 123 124 if previousItemId == None: 125 if self._last is not None: 126 self._next[self._last] = idd 127 self._prev[idd] = self._last 128 self._last = idd 129 else: 130 self._first = self._last = idd 131 else: 132 if (self._last == previousItemId) or (self._last is None): 133 self.addToOrderWrapper(idd) 134 elif previousItemId is None: 135 self._next[idd] = self._first 136 self._prev[self._first] = idd 137 self._first = idd 138 else: 139 self._prev[idd] = previousItemId 140 self._next[idd] = self._next[previousItemId] 141 self._prev[self._next.get(previousItemId)] = idd 142 self._next[previousItemId] = idd
143 144
145 - def updateOrderWrapper(self):
146 """Updates the wrapper's internal ordering information to include all 147 Items in the underlying container. 148 149 Note: If the contents of the wrapped container change without the 150 wrapper's knowledge, this method needs to be called to update the 151 ordering information of the Items. 152 """ 153 # Gets the first item stored in the ordered container. 154 if not self._ordered: 155 ids = self._container.getItemIds() 156 # Recreates ordering if some parts of it are missing 157 if (self._next is None or self._first is None 158 or self._last is None or self._prev is not None): 159 self._first = None 160 self._last = None 161 self._next = dict() 162 self._prev = dict() 163 164 # Filter out all the missing items 165 for idd in self._next: 166 if not self._container.containsId(idd): 167 self.removeFromOrderWrapper(idd) 168 169 # Adds missing items 170 for idd in ids: 171 if idd not in self._next: 172 self.addToOrderWrapper(idd)
173 174
175 - def firstItemId(self):
176 # Tests if the given item is the first item in the container. 177 if self._ordered: 178 return self._container.firstItemId() 179 return self._first
180 181
182 - def isFirstId(self, itemId):
183 # Tests if the given item is the last item in the container. 184 if self._ordered: 185 return self._container.isFirstId(itemId) 186 return self._first is not None and self._first == itemId
187 188
189 - def isLastId(self, itemId):
190 # Gets the last item stored in the ordered container. 191 if self._ordered: 192 return self._container.isLastId(itemId) 193 return self._last is not None and self._last == itemId
194 195
196 - def lastItemId(self):
197 # Gets the item that is next from the specified item. 198 if self._ordered: 199 return self._container.lastItemId() 200 return self._last
201 202
203 - def nextItemId(self, itemId):
204 # Gets the item that is previous from the specified item. 205 if self._ordered: 206 return self._container.nextItemId(itemId) 207 208 if itemId is None: 209 return None 210 211 return self._next.get(itemId)
212 213
214 - def prevItemId(self, itemId):
215 if self._ordered: 216 return self._container.prevItemId(itemId) 217 218 if itemId is None: 219 return None 220 221 return self._prev.get(itemId)
222 223
224 - def addContainerProperty(self, propertyId, typ, defaultValue):
225 """Registers a new Property to all Items in the Container. 226 227 @param propertyId: 228 the ID of the new Property. 229 @param typ: 230 the Data type of the new Property. 231 @param defaultValue: 232 the value all created Properties are initialized to. 233 @return: C{True} if the operation succeeded, C{False} if not 234 """ 235 return self._container.addContainerProperty(propertyId, typ, 236 defaultValue)
237 238
239 - def addItem(self, itemId=None):
240 """Creates a new Item into the Container, assigns it an automatic ID, 241 and adds it to the ordering. Alternatively, registers a new Item by 242 its ID to the underlying container and to the ordering. 243 244 @param itemId: 245 the ID of the Item to be created. 246 @return: 247 C{None} if the operation failed 248 @raise NotImplementedError: 249 if the addItem is not supported. 250 """ 251 if itemId is None: 252 idd = self._container.addItem() 253 if not self._ordered and (idd is not None): 254 self.addToOrderWrapper(idd) 255 return idd 256 else: 257 item = self._container.addItem(itemId) 258 if not self._ordered and (item is not None): 259 self.addToOrderWrapper(itemId) 260 return item
261 262
263 - def removeAllItems(self):
264 """Removes all items from the underlying container and from the 265 ordering. 266 267 @return: C{True} if the operation succeeded, otherwise C{False} 268 @raise NotImplementedError: 269 if the removeAllItems is not supported. 270 """ 271 success = self._container.removeAllItems() 272 if (not self._ordered) and success: 273 self._first = self._last = None 274 self._next.clear() 275 self._prev.clear() 276 return success
277 278
279 - def removeItem(self, itemId):
280 """Removes an Item specified by the itemId from the underlying 281 container and from the ordering. 282 283 @param itemId: 284 the ID of the Item to be removed. 285 @return: C{True} if the operation succeeded, C{False} if not 286 @raise NotImplementedError: 287 if the removeItem is not supported. 288 """ 289 success = self._container.removeItem(itemId) 290 if not self._ordered and success: 291 self.removeFromOrderWrapper(itemId) 292 return success
293 294
295 - def removeContainerProperty(self, propertyId):
296 """Removes the specified Property from the underlying container and 297 from the ordering. 298 299 Note: The Property will be removed from all the Items in the Container. 300 301 @param propertyId: 302 the ID of the Property to remove. 303 @return: C{True} if the operation succeeded, C{False} if not 304 @raise NotImplementedError: 305 if the removeContainerProperty is not supported. 306 """ 307 return self._container.removeContainerProperty(propertyId)
308 309
310 - def containsId(self, itemId):
311 # Does the container contain the specified Item? 312 return self._container.containsId(itemId)
313 314
315 - def getItem(self, itemId):
316 # Gets the specified Item from the container. 317 return self._container.getItem(itemId)
318 319
320 - def getItemIds(self):
321 # Gets the ID's of all Items stored in the Container 322 return self._container.getItemIds()
323 324
325 - def getContainerProperty(self, itemId, propertyId):
326 # Gets the Property identified by the given itemId and propertyId 327 # from the Container. 328 return self._container.getContainerProperty(itemId, propertyId)
329 330
331 - def getContainerPropertyIds(self):
332 # Gets the ID's of all Properties stored in the Container 333 return self._container.getContainerPropertyIds()
334 335
336 - def getType(self, propertyId):
337 # Gets the data type of all Properties identified by the given 338 # Property ID. 339 return self._container.getType(propertyId)
340 341
342 - def size(self):
343 # Gets the number of Items in the Container. 344 newSize = len(self._container) 345 if (self._lastKnownSize != -1 and newSize != self._lastKnownSize 346 and not isinstance(self._container, IItemSetChangeNotifier)): 347 # Update the internal cache when the size of the container changes 348 # and the container is incapable of sending ItemSetChangeEvents 349 self.updateOrderWrapper() 350 self._lastKnownSize = newSize 351 return newSize
352 353
354 - def __len__(self):
355 return self.size()
356 357
358 - def addListener(self, listener, iface=None):
359 if (isinstance(listener, IItemSetChangeListener) and 360 (iface is None or issubclass(iface, IItemSetChangeListener))): 361 # Registers a new Item set change listener for this Container. 362 if isinstance(self._container, IItemSetChangeNotifier): 363 pl = PiggybackListener(listener, self) 364 self._container.addListener(pl, IItemSetChangeListener) 365 366 if (isinstance(listener, IPropertySetChangeListener) and 367 (iface is None or 368 issubclass(iface, IPropertySetChangeListener))): 369 # Registers a new Property set change listener for this Container. 370 if isinstance(self._container, IPropertySetChangeNotifier): 371 pl = PiggybackListener(listener, self) 372 self._container.addListener(pl, IPropertySetChangeListener)
373 374
375 - def addCallback(self, callback, eventType=None, *args):
376 if eventType is None: 377 eventType = callback._eventType 378 379 if issubclass(eventType, IItemSetChangeEvent): 380 # Registers a new Item set change listener for this Container. 381 if isinstance(self._container, IItemSetChangeNotifier): 382 pl = PiggybackListener(callback, self, *args) 383 self._container.addListener(pl, IItemSetChangeListener) 384 385 elif issubclass(eventType, IPropertySetChangeEvent): 386 # Registers a new Property set change listener for this Container. 387 if isinstance(self._container, IPropertySetChangeNotifier): 388 pl = PiggybackListener(callback, self, *args) 389 self._container.addListener(pl, IPropertySetChangeListener) 390 391 else: 392 super(ContainerOrderedWrapper, self).addCallback(callback, 393 eventType, *args)
394 395
396 - def removeListener(self, listener, iface=None):
397 if (isinstance(listener, IItemSetChangeListener) and 398 (iface is None or issubclass(iface, IItemSetChangeListener))): 399 # Removes a Item set change listener from the object. 400 if isinstance(self._container, IItemSetChangeNotifier): 401 pl = PiggybackListener(listener, self) 402 self._container.removeListener(pl, IItemSetChangeListener) 403 404 405 if (isinstance(listener, IPropertySetChangeListener) and 406 (iface is None or issubclass(iface, IPropertySetChangeListener))): 407 # Removes a Property set change listener from the object. 408 if isinstance(self._container, IPropertySetChangeNotifier): 409 pl = PiggybackListener(listener, self) 410 self._container.removeListener(pl, IPropertySetChangeListener)
411 412
413 - def removeCallback(self, callback, eventType=None):
414 if eventType is None: 415 eventType = callback._eventType 416 417 if issubclass(eventType, IItemSetChangeEvent): 418 # Removes a Item set change listener from the object. 419 if isinstance(self._container, IItemSetChangeNotifier): 420 pl = PiggybackListener(callback, self) 421 self._container.removeListener(pl, IItemSetChangeListener) 422 423 elif issubclass(eventType, IPropertySetChangeEvent): 424 # Removes a Property set change listener from the object. 425 if isinstance(self._container, IPropertySetChangeNotifier): 426 pl = PiggybackListener(callback, self) 427 self._container.removeListener(pl, IPropertySetChangeListener) 428 429 else: 430 super(ContainerOrderedWrapper, self).removeCallback(callback, 431 eventType)
432 433
434 - def addItemAfter(self, previousItemId, newItemId=None):
435 # If the previous item is not in the container, fail 436 437 if newItemId == None: 438 if ((previousItemId is not None) 439 and not self.containsId(previousItemId)): 440 return None 441 442 # Adds the item to container 443 idd = self._container.addItem() 444 445 # Puts the new item to its correct place 446 if not self._ordered and idd is not None: 447 self.addToOrderWrapper(idd, previousItemId) 448 449 return idd 450 else: 451 if ((previousItemId is not None) 452 and not self.containsId(previousItemId)): 453 return None 454 455 # Adds the item to container 456 item = self._container.addItem(newItemId) 457 458 # Puts the new item to its correct place 459 if not self._ordered and item is not None: 460 self.addToOrderWrapper(newItemId, previousItemId) 461 462 return item
463 464
465 -class PiggybackListener(IPropertySetChangeListener, IItemSetChangeListener):
466 """This listener 'piggybacks' on the real listener in order to update the 467 wrapper when needed. It proxies __eq__() and __hash__() to the real 468 listener so that the correct listener gets removed. 469 """ 470
471 - def __init__(self, realListener, wrapper, *args):
472 self._listener = realListener 473 self._wrapper = wrapper 474 self._args = args
475 476
477 - def containerItemSetChange(self, event):
478 self._wrapper.updateOrderWrapper() 479 if isinstance(self._listener, IItemSetChangeListener): 480 self._listener.containerItemSetChange(event) 481 else: 482 self._listener(event, *self._args)
483 484
485 - def containerPropertySetChange(self, event):
486 self._wrapper.updateOrderWrapper() 487 if isinstance(self._listener, IPropertySetChangeListener): 488 self._listener.containerPropertySetChange(event) 489 else: 490 self._listener(event, *self._args)
491 492
493 - def __eq__(self, obj):
494 return ((obj == self._listener) 495 or (obj is not None and obj == self._listener))
496 497
498 - def __hash__(self):
499 return hash(self._listener)
500