1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """An implementation of the IIndexed interface with all important features."""
17
18 from muntjac.data import property as prop
19
20 from muntjac.data.item import IItem
21
22 from muntjac.data.util.abstract_in_memory_container import \
23 AbstractInMemoryContainer
24
25 from muntjac.data.util.filter.simple_string_filter import \
26 SimpleStringFilter
27
28 from muntjac.data.util.filter.unsupported_filter_exception import \
29 UnsupportedFilterException
30
31 from muntjac.data.util.abstract_container import \
32 BaseItemSetChangeEvent, AbstractContainer
33
34 from muntjac.data import container
35 from muntjac.util import EventObject
36 from muntjac.util import fullname
37
38
39
40 -class IndexedContainer(AbstractInMemoryContainer,
41 container.IPropertySetChangeNotifier,
42 prop.IValueChangeNotifier, container.ISortable,
43 container.IFilterable, container.ISimpleFilterable):
44 """An implementation of the L{IContainer.Indexed} interface with all
45 important features.
46
47 Features:
48 - L{IIndexed}
49 - L{IOrdered}
50 - L{ISortable}
51 - L{IFilterable}
52 - L{ICloneable} (deprecated, might be removed in the future)
53 - Sends all needed events on content changes.
54
55 @see: L{IContainer}
56 @author: Vaadin Ltd.
57 @author: Richard Lincoln
58 @version: 1.1.2
59 """
60
62
63
64 self._propertyIds = list()
65
66
67 self._types = dict()
68
69
70
71 self._items = dict()
72
73
74 self._readOnlyProperties = set()
75
76
77
78 self._propertyValueChangeListeners = list()
79
80 self._propertyValueChangeCallbacks = dict()
81
82
83
84
85
86
87 self._singlePropertyValueChangeListeners = dict()
88
89 self._defaultPropertyValues = dict()
90
91 self._nextGeneratedItemId = 1
92
93 super(IndexedContainer, self).__init__()
94
95 if itemIds is not None:
96 for itemId in itemIds:
97 self.internalAddItemAtEnd(itemId,
98 IndexedContainerItem(itemId, self), False)
99
100 self.filterAll()
101
102
103
105 if itemId is not None and itemId in self._items:
106 return IndexedContainerItem(itemId, self)
107
108 return None
109
110
112 return list(self._propertyIds)
113
114
116 """Gets the type of a IProperty stored in the list.
117
118 @param propertyId:
119 the ID of the IProperty.
120 @return: Type of the requested IProperty
121 """
122 return self._types.get(propertyId)
123
124
130
131
133
134 if propertyId is None or typ is None:
135 return False
136
137
138 if propertyId in self._propertyIds:
139 return False
140
141
142 self._propertyIds.append(propertyId)
143 self._types[propertyId] = typ
144
145
146 if defaultValue is not None:
147
148 for item in self.getAllItemIds():
149 prop = self.getItem(item).getItemProperty(propertyId)
150 prop.setValue(defaultValue)
151
152
153 if self._defaultPropertyValues is None:
154 self._defaultPropertyValues = dict()
155
156 self._defaultPropertyValues[propertyId] = defaultValue
157
158
159 self.fireContainerPropertySetChange()
160
161 return True
162
163
178
179
199
200
202 """Helper method to add default values for items if available
203
204 @param t: data table of added item
205 """
206 if self._defaultPropertyValues is not None:
207 for key in self._defaultPropertyValues.keys():
208 t[key] = self._defaultPropertyValues.get(key)
209
210
212 if itemId is None or itemId not in self._items:
213 return False
214 else:
215 del self._items[itemId]
216
217 origSize = self.size()
218 position = self.indexOfId(itemId)
219 if self.internalRemoveItem(itemId):
220
221
222 if self.size() != origSize:
223 self.fireItemRemoved(position, itemId)
224
225 return True
226 else:
227 return False
228
229
231
232
233 if propertyId not in self._propertyIds:
234 return False
235
236
237 self._propertyIds.remove(propertyId)
238 if propertyId in self._types:
239 del self._types[propertyId]
240 if self._defaultPropertyValues is not None:
241 if propertyId in self._defaultPropertyValues:
242 del self._defaultPropertyValues[propertyId]
243
244
245 for item in self.getAllItemIds():
246 self._items.get(item).remove(propertyId)
247
248
249 self.fireContainerPropertySetChange()
250
251 return True
252
253
264
265
275
276
278 """Generates an unique identifier for use as an item id. Guarantees
279 that the generated id is not currently used as an id.
280 """
281 while True:
282 idd = int(self._nextGeneratedItemId)
283 self._nextGeneratedItemId += 1
284 if idd not in self._items:
285 break
286 return idd
287
288
293
294
309
310
311 - def addCallback(self, callback, eventType=None, *args):
325
326
341
342
356
357
359 """Sends a IProperty value change event to all interested listeners.
360
361 @param source:
362 the IndexedContainerProperty object.
363 """
364
365 event = PropertyValueChangeEvent(source)
366 for listener in self._propertyValueChangeListeners:
367 listener.valueChange(event)
368
369 for callback, args in self._propertyValueChangeCallbacks.iteritems():
370 callback(event, *args)
371
372
373 propertySetToListenerListMap = \
374 self._singlePropertyValueChangeListeners.get(source._propertyId)
375
376 if propertySetToListenerListMap is not None:
377 listenerList = propertySetToListenerListMap.get(source._itemId)
378 if listenerList is not None:
379 event = PropertyValueChangeEvent(source)
380 for l in listenerList:
381 l.valueChange(event)
382
383
389
390
396
397
402
403
410
411
413 """Adds new single IProperty change listener.
414
415 @param propertyId:
416 the ID of the IProperty to add.
417 @param itemId:
418 the ID of the IItem .
419 @param listener:
420 the listener to be added.
421 """
422 if listener is not None:
423 if self._singlePropertyValueChangeListeners is None:
424 self._singlePropertyValueChangeListeners = dict()
425
426 propertySetToListenerListMap = \
427 self._singlePropertyValueChangeListeners.get(propertyId)
428
429 if propertySetToListenerListMap is None:
430 propertySetToListenerListMap = dict()
431 self._singlePropertyValueChangeListeners[propertyId] = \
432 propertySetToListenerListMap
433
434 listenerList = propertySetToListenerListMap.get(itemId)
435
436 if listenerList is None:
437 listenerList = list()
438 propertySetToListenerListMap[itemId] = listenerList
439
440 listenerList.append(listener)
441
442
444 """Removes a previously registered single IProperty change listener.
445
446 @param propertyId:
447 the ID of the IProperty to remove.
448 @param itemId:
449 the ID of the IItem.
450 @param listener:
451 the listener to be removed.
452 """
453 if (listener is not None
454 and self._singlePropertyValueChangeListeners is not None):
455
456 propertySetToListenerListMap = \
457 self._singlePropertyValueChangeListeners.get(propertyId)
458
459 if propertySetToListenerListMap is not None:
460 listenerList = propertySetToListenerListMap.get(itemId)
461
462 if listenerList is not None and listener in listenerList:
463 listenerList.remove(listener)
464
465 if len(listenerList) == 0:
466 if itemId in propertySetToListenerListMap:
467 del propertySetToListenerListMap[itemId]
468
469 if len(propertySetToListenerListMap) == 0:
470 if propertyId in self._singlePropertyValueChangeListeners:
471 del self._singlePropertyValueChangeListeners[propertyId]
472
473 if len(self._singlePropertyValueChangeListeners) == 0:
474 self._singlePropertyValueChangeListeners = None
475
476
477 - def sort(self, propertyId, ascending):
479
480
483
484
487
488
491
492
563
564
566 nargs = len(args)
567 if nargs == 1:
568 fltr, = args
569 self.addFilter(fltr)
570 elif nargs == 4:
571 propertyId, filterString, ignoreCase, onlyMatchPrefix = args
572
573
574 try:
575 self.addFilter(SimpleStringFilter(propertyId, filterString,
576 ignoreCase, onlyMatchPrefix))
577 except UnsupportedFilterException:
578 pass
579 else:
580 raise ValueError, 'invalid number of elements'
581
582
585
586
589
590
593
594
596
598 """Constructs a new ListItem instance and connects it to a host
599 container.
600
601 @param itemId: the IItem ID of the new IItem.
602 """
603 if itemId is None:
604 raise ValueError
605
606
607 self._itemId = itemId
608
609 self._container = container
610
611
614
615
617 return list(self._container._propertyIds)
618
619
621 """Gets the string representation of the contents of the IItem. The
622 format of the string is a space separated catenation of the string
623 representations of the Properties contained by the IItem.
624
625 @return: string representation of the IItem contents
626 """
627 retValue = ''
628
629 for i, propertyId in enumerate(self._container._propertyIds):
630 retValue += str(self.getItemProperty(propertyId))
631 if i < len(self._container._propertyIds) - 1:
632 retValue += ' '
633
634 return retValue
635
636
638 """Calculates a integer hash-code for the IItem that's unique inside
639 the list. Two Items inside the same list have always different
640 hash-codes, though Items in different lists may have identical
641 hash-codes.
642
643 @return: A locally unique hash-code as integer
644 """
645 return hash(self._itemId)
646
647
649 """Tests if the given object is the same as the this object. Two Items
650 got from a list container with the same ID are equal.
651
652 @param obj:
653 an object to compare with this object
654 @return: C{True} if the given object is the same as this
655 object, C{False} if not
656 """
657 if obj is None or obj.__class__ != IndexedContainerItem:
658 return False
659
660 li = obj
661
662 return self.getHost() == li.getHost() and self._itemId == li._itemId
663
664
666 return self._container
667
668
670 """IndexedContainerItem does not support adding new properties. Add
671 properties at container level. See
672 L{IndexedContainer.addContainerProperty}
673
674 @see: L{IItem.addProperty}
675 """
676 raise NotImplementedError, ('Indexed container item '
677 + 'does not support adding new properties')
678
679
681 """Indexed container does not support removing properties. Remove
682 properties at container level. See
683 L{IndexedContainer.removeContainerProperty}
684
685 @see: IItem.removeProperty
686 """
687 raise NotImplementedError, \
688 'Indexed container item does not support property removal'
689
690
692 """A class implementing the L{IProperty} interface to be contained in
693 the L{IndexedContainerItem} contained in the L{IndexedContainer}.
694
695 @author: Vaadin Ltd.
696 @author: Richard Lincoln
697 @version: 1.1.2
698 """
699
700 - def __init__(self, itemId, propertyId, container):
701 """Constructs a new L{IndexedContainerProperty} object.
702
703 @param itemId:
704 the ID of the IItem to connect the new IProperty to.
705 @param propertyId:
706 the IProperty ID of the new IProperty.
707 @param container:
708 the list that contains the IItem to contain the new
709 IProperty.
710 """
711 if (itemId is None) or (propertyId is None):
712
713 raise ValueError, 'IContainer item or property ids can not be null'
714
715
716 self._propertyId = propertyId
717
718
719 self._itemId = itemId
720
721 self._container = container
722
723
725 return self._container._types.get(self._propertyId)
726
727
729 return self._container._items.get(self._itemId).get(self._propertyId)
730
731
733 return self in self._container._readOnlyProperties
734
735
737 if newStatus:
738 self._container._readOnlyProperties.add(self)
739 else:
740 self._container._readOnlyProperties.remove(self)
741
742
744
745 propertySet = self._container._items.get(self._itemId)
746
747
748 if newValue is None:
749 if self._propertyId in propertySet:
750 del propertySet[self._propertyId]
751 elif issubclass(newValue.__class__, self.getType()):
752 propertySet[self._propertyId] = newValue
753 else:
754 try:
755
756
757 constr = self.getType().__init__
758
759 propertySet[self._propertyId] = constr(*[str(newValue)])
760 except Exception:
761 raise prop.ConversionException, ('Conversion for value \''
762 + newValue + '\' of class ' + fullname(newValue)
763 + ' to ' + self.getType().__name__ + ' failed')
764
765
766 if self._container.isPropertyFiltered(self._propertyId):
767 self._container.filterAll()
768
769 self._container.firePropertyValueChange(self)
770
771
773 """Returns the value of the IProperty in human readable textual format.
774 The return value should be assignable to the C{setValue} method if the
775 IProperty is not in read-only mode.
776
777 @return: String representation of the value stored in the IProperty
778 """
779 value = self.getValue()
780
781 if value is None:
782 return ''
783
784 return str(value)
785
786
788 """Calculates a integer hash-code for the IProperty that's unique inside
789 the IItem containing the IProperty. Two different Properties inside the
790 same IItem contained in the same list always have different
791 hash-codes, though Properties in different Items may have identical
792 hash-codes.
793
794 @return: A locally unique hash-code as integer
795 """
796 return hash(self._itemId) ^ hash(self._propertyId)
797
798
800 """Tests if the given object is the same as the this object. Two
801 Properties got from an IItem with the same ID are equal.
802
803 @param obj:
804 an object to compare with this object
805 @return: C{True} if the given object is the same as this
806 object, C{False} if not
807 """
808 if obj is None or obj.__class__ != IndexedContainerProperty:
809 return False
810
811 lp = obj
812
813 return (lp.getHost() == self.getHost()
814 and lp._propertyId == self._propertyId
815 and lp._itemId == self._itemId)
816
817
823
824
825 - def addCallback(self, callback, eventType=None, *args):
835
836
842
843
854
855
857 return self._container
858
859
861 """An C{Event} object specifying the list whose IItem set has changed.
862
863 @author: Vaadin Ltd.
864 @author: Richard Lincoln
865 @version: 1.1.2
866 """
867
868 - def __init__(self, source, addedItemIndex):
871
872
874 """Iff one item is added, gives its index.
875
876 @return: -1 if either multiple items are changed or some other change
877 than add is done.
878 """
879 return self._addedItemIndex
880
881
883 """An C{Event} object specifying the IProperty in a list whose
884 value has changed.
885
886 @author: Vaadin Ltd.
887 @author: Richard Lincoln
888 @version: 1.1.2
889 """
890
893
894
897