Changeset 445 for trunk/gaphor/uml2
- Timestamp:
- 11/02/04 02:52:18 (4 years ago)
- Files:
-
- trunk/gaphor/uml2/collection.py (modified) (10 diffs)
- trunk/gaphor/uml2/element.py (modified) (7 diffs)
- trunk/gaphor/uml2/properties.py (modified) (18 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/gaphor/uml2/collection.py
r192 r445 1 # vi m:sw=41 # vi:sw=4:et 2 2 3 3 import inspect … … 6 6 pass 7 7 8 class Collection(object):8 class collection(object): 9 9 10 10 def __init__(self, property, object, type): 11 self.property = property12 self.object = object13 self.type = type14 self.items = []11 self.property = property 12 self.object = object 13 self.type = type 14 self.items = [] 15 15 16 16 def __len__(self): … … 18 18 19 19 def __setitem__(self, key, value): 20 raise CollectionError, 'items should not be overwritten.'20 raise CollectionError, 'items should not be overwritten.' 21 21 22 22 def __delitem__(self, key): … … 30 30 31 31 def __setslice__(self, i, j, s): 32 raise CollectionError, 'items should not be overwritten.'32 raise CollectionError, 'items should not be overwritten.' 33 33 34 34 def __delslice__(self, i, j): 35 raise CollectionError, 'items should not be deleted this way.'35 raise CollectionError, 'items should not be deleted this way.' 36 36 37 37 def __contains__(self, obj): … … 39 39 40 40 def __iter__(self): 41 return iter(self.items)41 return iter(self.items) 42 42 43 43 def __str__(self): 44 return str(self.items) 44 return str(self.items) 45 46 __repr__ = __str__ 45 47 46 48 def __nonzero__(self): 47 return self.items!=[]49 return self.items!=[] 48 50 49 51 def append(self, value): 50 if isinstance(value, self.type):51 self.property._set(self.object, value)52 else:53 raise CollectionError, 'Object is not of type %s' % self.type.__name__52 if isinstance(value, self.type): 53 self.property._set(self.object, value) 54 else: 55 raise CollectionError, 'Object is not of type %s' % self.type.__name__ 54 56 55 57 def remove(self, value): 56 if value in self.items: 57 self.property.__delete__(self.object, value) 58 else: 59 raise AttributeError, '%s not in collection' % value 58 if value in self.items: 59 self.property.__delete__(self.object, value) 60 else: 61 raise AttributeError, '%s not in collection' % value 62 60 63 61 64 def index(self, key): 62 """Given an object, return the position of that object in the 63 collection.""" 64 return self.items.index(key) 65 65 """Given an object, return the position of that object in the 66 collection.""" 67 return self.items.index(key) 68 69 66 70 # OCL members (from SMW by Ivan Porres, http://www.abo.fi/~iporres/smw) 67 71 … … 128 132 def forAll(self,f): 129 133 if not self.items or not inspect.getargspec(f)[0]: 130 return 1134 return True 131 135 132 136 nargs=len(inspect.getargspec(f)[0]) … … 143 147 args.append(self.items[x]) 144 148 if not apply(f,args): 145 return 0149 return False 146 150 c=len(index)-1 147 151 index[c]=index[c]+1 … … 150 154 c=c-1 151 155 if c<0: 152 return 1156 return True 153 157 else: 154 158 index[c]=index[c]+1 155 159 if index[c]==nitems-1: 156 160 c=c-1 161 return False 157 162 158 163 def exist(self,f): 159 164 if not self.items or not inspect.getargspec(f)[0]: 160 return 0165 return False 161 166 162 167 nargs=len(inspect.getargspec(f)[0]) … … 172 177 args.append(self.items[x]) 173 178 if apply(f,args): 174 return 1179 return True 175 180 c=len(index)-1 176 181 index[c]=index[c]+1 … … 179 184 c=c-1 180 185 if c<0: 181 return 0186 return False 182 187 else: 183 188 index[c]=index[c]+1 184 189 if index[c]==nitems-1: 185 190 c=c-1 191 return False 192 193 194 def moveUp(self, value): 195 """ 196 Move element up. Owner is notified about the change. 197 """ 198 i1 = self.items.index(value) 199 i2 = i1 - 1 200 if i2 >= 0: 201 self.items[i1], self.items[i2] = self.items[i2], self.items[i1] 202 self.property.notify(self.object) # send a notification that this list has changed 203 else: 204 log.warning('Cannot move up first element') 205 206 207 def moveDown(self, value): 208 """ 209 Move element down. Owner is notified about the change. 210 """ 211 i1 = self.items.index(value) 212 i2 = i1 + 1 213 if i2 < len(self.items): 214 self.items[i1], self.items[i2] = self.items[i2], self.items[i1] 215 self.property.notify(self.object) # send a notification that this list has changed 216 else: 217 log.warning('Cannot move down last element') trunk/gaphor/uml2/element.py
r196 r445 6 6 with a completely new data model here. 7 7 8 save()/load()/postload() 8 save(save_func) 9 load(name, value) 10 postload() 9 11 Load/save the element. 10 12 11 13 unlink() 12 Remove all references to the element. First of all the signal 13 '__unlink__' is send to all attached signals. 14 15 attach ('name', callback, *data) or 16 attach (('name', 'other_property'), callback, *data) 14 Remove all references to the element. This is done by emiting the 15 '__unlink__' signal to all attached signals. unlink() can not be called 16 recursively. 17 #relink() 18 # Inverse operation of unlink(). Used by diagram items during undo operations. 19 20 connect ('name', callback, *data) or 21 connect (('name', 'other_property'), callback, *data) 17 22 Connect 'callback' to recieve notifications if one of the properties 18 23 change (the latter being more memory efficient if you want to listen on … … 20 25 first argument, followed by *data. 21 26 22 d etach(callback, *data)27 disconnect (callback, *data) 23 28 Disconnect callback as listener. 24 29 25 30 notify (name) 26 31 Notify all listeners the property 'name' has changed. 32 The notifier calls callbacks registered by connect() by sending 33 callback(self, pspec, *data) 34 where self is the data object (Element) and pspec is the property spec. 27 35 """ 28 36 29 import types 37 __all__ = [ 'Element' ] 38 39 import types, mutex 40 import gaphor.misc.uniqueid as uniqueid 41 from properties import umlproperty, association 30 42 31 43 class Element(object): 32 44 """Base class for UML data classes.""" 33 45 34 def __init__(self): 35 self._observers = dict() 46 def __init__(self, id=None, factory=None): 47 self.id = id or uniqueid.generate_id() 48 # The factory this element belongs to. 49 self._factory = factory 50 self._observers = dict() 51 self.__in_unlink = mutex.mutex() 52 53 factory = property(lambda self: self._factory, 54 doc="The factory that created this element") 36 55 37 56 def save(self, save_func): 38 """Save the state by calling save_func(name, value).""" 39 lst = dir(self.__class__) 40 for prop in map(isinstance, lst, [umlproperty] * len(lst)): 57 """Save the state by calling save_func(name, value).""" 58 umlprop = umlproperty 59 clazz = type(self) 60 for propname in dir(clazz): 61 prop = getattr(clazz, propname) 62 if isinstance(prop, umlprop): 41 63 prop.save(self, save_func) 42 64 43 65 def load(self, name, value): 44 """Loads value in name. Make sure that for every load postload() 45 should be called.""" 46 try: 47 prop = getattr(self.__class__, name) 66 """Loads value in name. Make sure that for every load postload() 67 should be called. 68 """ 69 try: 70 prop = getattr(type(self), name) 48 71 except AttributeError, e: 49 72 raise AttributeError, "'%s' has no property '%s'" % \ 50 ( self.__class__.__name__, name)73 (type(self).__name__, name) 51 74 else: 52 75 prop.load(self, value) 53 76 54 77 def postload(self): 55 pass 78 """Fix up the odds and ends. 79 """ 80 for name in dir(type(self)): 81 try: 82 prop = getattr(type(self), name) 83 except AttributeError, e: 84 raise AttributeError, "'%s' has no property '%s'" % \ 85 (type(self).__name__, name) 86 else: 87 if isinstance(prop, umlproperty): 88 prop.postload(self) 89 90 def __unlink(self, signal): 91 """Unlink the element. For both the __unlink__ and __relink__ signal 92 the __unlink__ callback list is used. 93 """ 94 # Uses a mutex to make sure it is not called recursively 95 if self.__in_unlink.testandset(): 96 try: 97 self.notify(signal, '__unlink__') 98 finally: 99 self.__in_unlink.unlock() 56 100 57 101 def unlink(self): 58 self.notify('__unlink__') 59 60 def attach(self, names, callback, *data): 61 """Attach 'callback' to a list of names. Names may also be a string.""" 102 """Unlink the element.""" 103 #log.debug('Element.unlink(%s)' % self) 104 self.__unlink('__unlink__') 105 106 # def relink(self): 107 # """Undo the unlink operation.""" 108 # log.debug('Element.relink(%s)' % self) 109 # self.__unlink('__relink__') 110 111 def connect(self, names, callback, *data): 112 """Attach 'callback' to a list of names. Names may also be a string. 113 A name is the name od a property of the object or '__unlink__'. 114 """ 115 #log.debug('Element.connect(%s, %s, %s)' % (names, callback, data)) 62 116 if type(names) is types.StringType: 63 117 names = (names,) … … 72 126 self._observers[name] = [cb] 73 127 74 def detach(self, callback, *data): 75 """Detach a callback identified by it's data.""" 76 #print 'detach', callback, data 128 def disconnect(self, callback, *data): 129 """Detach a callback identified by it's data.""" 77 130 cb = (callback,) + data 78 for values in self._observers.values():131 for values in self._observers.values(): 79 132 # Remove all occurences of 'cb' from values 80 133 # (if none is found ValueError is raised). … … 85 138 pass 86 139 87 def notify(self, name ):140 def notify(self, name, cb_name=None, pspec=None): 88 141 """Send notification to attached callbacks that a property 89 has changed.""" 90 cb_list = self._observers.get(name) or () 91 for cb_data in cb_list: 92 try: 93 apply(cb_data[0], (name,) + cb_data[1:]) 94 except: 95 pass 142 has changed. the __relink__ signal uses the callbacks for __unlink__. 143 """ 144 cb_list = self._observers.get(cb_name or name, ()) 145 #log.debug('Element.notify: %s' % cb_list) 146 if not pspec: 147 try: 148 pspec = getattr(type(self), name) 149 except AttributeError: 150 pspec = name 151 152 # Use a copy of the list to ensure all items are notified 153 for cb_data in list(cb_list): 154 try: 155 apply(cb_data[0], (self, pspec) + cb_data[1:]) 156 except Exception, e: 157 log.error('failed notification for %s' % cb_data[0], e) 96 158 97 159 # OCL methods: (from SMW by Ivan Porres (http://www.abo.fi/~iporres/smw)) 98 160 99 def isKindOf(self, clazz):161 def isKindOf(self, class_): 100 162 """Returns true if the object is an instance of clazz.""" 101 return isinstance(self, cla zz)163 return isinstance(self, class_) 102 164 103 165 def isTypeOf(self, other): … … 105 167 return type(self) == type(other) 106 168 169 # def __setattr__(self, key, value): 170 # if key.startswith('_') or key == 'id': 171 # object.__setattr__(self, key, value) 172 # else: 173 # raise AttributeError, 'Invalid attribute "%s"' % key 174 175 try: 176 import psyco 177 except ImportError: 178 pass 179 else: 180 psyco.bind(Element) 107 181 108 182 if __name__ == '__main__': … … 112 186 print ' cb_func:', name, args 113 187 114 a. attach('ev1', cb_func, a)115 a. attach('ev1', cb_func, a)116 a. attach('ev2', cb_func, 'ev2', a)188 a.connect('ev1', cb_func, a) 189 a.connect('ev1', cb_func, a) 190 a.connect('ev2', cb_func, 'ev2', a) 117 191 118 192 print 'notify: ev1' … … 121 195 a.notify('ev2') 122 196 123 a.d etach(cb_func, a)197 a.disconnect(cb_func, a) 124 198 125 199 print 'notify: ev1' trunk/gaphor/uml2/properties.py
r197 r445 24 24 load(value): load 'value' as the current value for this property 25 25 save(save_func): send the value of the property to save_func(name, value) 26 unlink(): remove references to other elements.26 #unlink(): remove references to other elements. 27 27 """ 28 28 29 from collection import Collection 29 __all__ = [ 'attribute', 'enumeration', 'association', 'derivedunion', 'redefine' ] 30 31 from collection import collection 30 32 import operator 33 from gaphor.undomanager import get_undo_manager 31 34 32 35 #infinite = 100000 36 37 class undoaction(object): 38 """This undo action contains one undo action for an attribute 39 property. 40 """ 41 42 def __init__(self, prop, obj, value): 43 self.prop = prop 44 self.obj = obj 45 self.value = value 46 #print 'adding new property', prop.name, obj, value 47 get_undo_manager().add_undo_action(self) 48 33 49 34 50 class umlproperty(object): … … 45 61 self.deriviates = [ deriviate ] 46 62 47 def __get__(self, obj, cla zz=None):63 def __get__(self, obj, class_=None): 48 64 if not obj: 49 65 return self … … 56 72 self._del(obj, value) 57 73 74 def save(self, obj, save_func): 75 if hasattr(obj, self._name): 76 save_func(self.name, self._get(obj)) 77 58 78 def load(self, obj, value): 59 79 self._set(obj, value) 60 80 61 def save(self, obj, save_func): 62 if hasattr(obj, '_' + self.name): 63 save_func(self.name, self._get(obj)) 64 65 def unlink(self, obj): 66 if hasattr(obj, '_' + self.name): 67 self.__delete__(obj) 81 def postload(self, obj): 82 pass 83 84 # def unlink(self, obj): 85 # if hasattr(obj, self._name): 86 # self.__delete__(obj) 68 87 69 88 def notify(self, obj): 70 89 """Notify obj that the property's value has been changed. 71 Deriviates are also triggered to send a notify signal.""" 72 try: 73 obj.notify(self.name) 74 except: 75 pass 90 Deriviates are also triggered to send a notify signal. 91 """ 92 try: 93 obj.notify(self.name, pspec=self) 94 except Exception, e: 95 log.error(str(e), e) 76 96 77 97 # we need to check if a deriviate is part of the current object: 78 98 # TODO: can we do this faster? 79 mro = obj.__class__.__mro__ 80 try: 81 for d in self.deriviates: 82 for c in mro: 83 if d in c.__dict__.values(): 99 try: 100 deriviates = self.deriviates 101 except AttributeError: 102 pass # no derivedunion or redefine attribute 103 else: 104 values = [] 105 for c in type(obj).__mro__: 106 values.extend(c.__dict__.values()) 107 108 for d in deriviates: 109 if d in values: 110 try: 84 111 d.notify(obj) 85 except AttributeError: 86 pass # no derivedunion or redefine attribute 112 except Exception, e: 113 log.error(e, e) 114 115 116 class undoattributeaction(undoaction): 117 """This undo action contains one undo action for an attribute 118 property. 119 """ 120 121 def undo(self): 122 self.redo_value = self.prop._get(self.obj) 123 setattr(self.obj, self.prop._name, self.value) 124 self.prop.notify(self.obj) 125 126 def redo(self): 127 setattr(self.obj, self.prop._name, self.redo_value) 128 self.prop.notify(self.obj) 87 129 88 130 … … 92 134 93 135 # TODO: check if lower and upper are actually needed for attributes 94 def __init__(self, name, type, default, lower=0, upper=1): 95 self.name = name 136 def __init__(self, name, type, default=None, lower=0, upper=1): 137 self.name = intern(name) 138 self._name = intern('_' + name) 96 139 self.type = type 97 140 self.default = default … … 100 143 101 144 def load(self, obj, value): 102 if not isinstance(value, self.type): 103 raise AttributeError, 'Value should be of type %s' % self.type.__name__ 104 setattr(obj, '_' + self.name, value) 145 # FixMe: value might be a string while some other type is required: 146 #print 'attribute.load:', self.name, self.type, value, 147 if self.type is not object: 148 value = self.type(value) 149 setattr(obj, self._name, value) 105 150 106 151 def __str__(self): … … 112 157 def _get(self, obj): 113 158 try: 114 return getattr(obj, '_' + self.name)159 return getattr(obj, self._name) 115 160 except AttributeError: 116 161 return self.default 117 162 118 163 def _set(self, obj, value): 119 if not isinstance(value, self.type): 120 raise AttributeError, 'Value should be of type %s' % self.type.__name__ 121 if value == self.default: 122 delattr(obj, '_' + self.name) 123 else: 124 setattr(obj, '_' + self.name, value) 164 if value is not None and not isinstance(value, self.type): 165 raise AttributeError, 'Value should be of type %s' % hasattr(self.type, '__name__') and self.type.__name__ or self.type 166 167 if value == self._get(obj): 168 return 169 170 undoattributeaction(self, obj, self._get(obj)) 171 172 if value == self.default and hasattr(obj, self._name): 173 delattr(obj, self._name) 174 else: 175 setattr(obj, self._name, value) 125 176 self.notify(obj) 126 177 127 178 def _del(self, obj, value=None): 128 delattr(obj, '_' + self.name) 129 self.notify(obj) 179 #self.old = self._get(obj) 180 try: 181 undoattributeaction(self, obj, self._get(obj)) 182 delattr(obj, self._name) 183 except AttributeError: 184 pass 185 else: 186 self.notify(obj) 130 187 131 188 … … 135 192 136 193 def __init__(self, name, values, default): 137 self.name = name 194 self.name = intern(name) 195 self._name = intern('_' + name) 138 196 self.values = values 139 197 self.default = default … … 144 202 def _get(self, obj): 145 203 try: 146 return getattr(obj, '_' + self.name)204 return getattr(obj, self._name) 147 205 except AttributeError: 148 206 return self.default … … 150 208 def load(self, obj, value): 151 209 if not value in self.values: 152 raise AttributeError, 'Value should be in%s' % str(self.values)153 setattr(obj, '_' + self.name, value)210 raise AttributeError, 'Value should be one of %s' % str(self.values) 211 setattr(obj, self._name, value) 154 212 155 213 def _set(self, obj, value): 156 214 if not value in self.values: 157 raise AttributeError, 'Value should be in%s' % str(self.values)215 raise AttributeError, 'Value should be one of %s' % str(self.values) 158 216 if value != self._get(obj): 217 undoattributeaction(self, obj, self._get(obj)) 159 218 if value == self.default: 160 delattr(obj, '_' + self.name)219 delattr(obj, self._name) 161 220 else: 162 setattr(obj, '_' + self.name, value)221 setattr(obj, self._name, value) 163 222 self.notify(obj) 164 223 165 224 def _del(self, obj, value=None): 166 delattr(obj, '_' + self.name) 167 self.notify(obj) 225 try: 226 undoattributeaction(self, obj, self._get(obj)) 227 delattr(obj, self._name) 228 except AttributeError: 229 pass 230 else: 231 self.notify(obj) 232 233 234 class undosetassociationaction(undoaction): 235 """Undo a 'set' action in an association. 236 """ 237 238 def undo(self): 239 #log.debug('undosetassociationaction del: %s %s %s' % (self.obj, self.prop.name, self.value)) 240 self.prop._del(self.obj, self.value) 241 242 def redo(self): 243 #log.debug('undosetassociationaction set: %s %s %s' % (self.obj, self.prop.name, self.value)) 244 self.prop._set(self.obj, self.value) 245 246 247 class undodelassociationaction(undoaction): 248 """Undo a 'del' action in an association. 249 """ 250 251 def undo(self): 252 #log.debug('undodelassociationaction set: %s %s %s' % (self.obj, self.prop.name, self.value)) 253 self.prop._set(self.obj, self.value) 254 255 def redo(self): 256 #log.debug('undodelassociationaction del: %s %s %s' % (self.obj, self.prop.name, self.value)) 257 self.prop._del(self.obj, self.value) 168 258 169 259 170 260 class association(umlproperty): 171 261 """Association, both uni- and bi-directional. 172 Element.assoc = association('assoc', Element, opposite='other')""" 173 174 def __init__(self, name, type, lower=0, upper='*', opposite=None): 175 self.name = name 262 Element.assoc = association('assoc', Element, opposite='other') 263 264 A listerer is connected to the value added to the association. This 265 will cause the association to be ended if the element on the other end 266 of the association is unlinked. 267 If the association is a composite relationship, the value is connected to 268 the elements __unlink__ signal too. This will cause the value to be 269 unlinked as soon as the element is unlinked. 270 """ 271 272 def __init__(self, name, type, lower=0, upper='*', composite=False, opposite=None): 273 self.name = intern(name) 274 self._name = intern('_' + name) 176 275 self.type = type 177 276 self.lower = lower 178 277 self.upper = upper 179 self.opposite = opposite 278 self.composite = composite 279 self.opposite = opposite and intern(opposite) 180 280 181 281 def load(self, obj, value): 182 282 if not isinstance(value, self.type): 183 raise AttributeError, 'Value should be of type %s' % self.type.__name__ 184 if self.upper > 1: 185 self._get(obj).items.append(value) 186 else: 187 setattr(obj, '_' + self.name, value) 283 raise AttributeError, 'Value for %s should be of type %s (%s)' % (self.name, self.type.__name__, type(value).__name__) 284 # TODO: avoid sending notifications: 285 self._set(obj, value, do_notify=False) 286 287 def postload(self, obj): 288 """In the postload step, ensure that bi-directional associations 289 are bi-directional. 290 """ 291 values = self._get(obj) 292 if not values: 293 return 294 if self.upper == 1: 295 values = [ values ] 296 for value in values: 297 if not isinstance(value, self.type): 298 raise AttributeError, 'Error in postload validation for %s: Value %s should be of type %s' % (self.name, value, self.type.__name__) 188 299 189 300 def __str__(self): … … 193 304 s = '<association %s: %s[%s..%s]' % (self.name, self.type.__name__, self.lower, self.upper) 194 305 if self.opposite: 195 s += ' -> %s' % self.opposite306 s += ' %s-> %s' % (self.composite and '<>' or '', self.opposite) 196 307 return s + '>' 197 308 198 def __get__(self, obj, clazz=None): 199 """Retrieve the value of the association. In case this is called 200 directly on the class, return self.""" 201 #print '__get__', self, obj, clazz 202 if not obj: 203 return self 204 return self._get(obj) 205 206 def __set__(self, obj, value): 309 def _get(self, obj): 310 #print '_get', self, obj 311 # TODO: Handle lower and add items if lower > 0 312 try: 313 return getattr(obj, self._name) 314 except AttributeError: 315 if self.upper > 1: 316 # Create the empty collection here since it might be used to 317 # add 318 #c = collection(self, obj, self.type) 319 #setattr(obj, self._name, c) 320 c = collection(self, obj, self.type) 321 setattr(obj, self._name, c) 322 return c 323 else: 324 return None 325 326 def _set(self, obj, value, from_opposite=False, do_notify=True): 207 327 """Set a new value for our attribute. If this is a collection, append 208 to the existing collection.""" 328 to the existing collection. 329 330 This method is called from the opposite association property. 331 """ 209 332 #print '__set__', self, obj, value 210 if not isinstance(value, self.type): 333 if not (isinstance(value, self.type) or \ 334 (value is None and self.upper == 1)): 211 335 raise AttributeError, 'Value should be of type %s' % self.type.__name__ 212 336 # Remove old value only for uni-directional associations 213 337 if self.upper == 1: 214 338 old = self._get(obj) 339 # do nothing if we are assigned our current value: 340 if value is old: 341 return 342 343 # is done is _del(): undoassociationaction(self, obj, old) 215 344 if old: 216 self.__delete__(obj, old) 217 # Set opposite side: 218 if self.opposite: 219 getattr(self.type, self.opposite)._set(value, obj) 220 self.__set(obj, value) 221 222 def __delete__(self, obj, value=None): 223 """Delete is used for element deletion and for removal of elements from 224 a list.""" 345 self._del(obj, old) 346 if value is None: 347 return 348 if value is self._get(obj): 349 #log.debug('association: value already in obj: %s' % value) 350 return 351 352 if not from_opposite: 353 undosetassociationaction(self, obj, value) 354 setattr(obj, self._name, value) 355 else: 356 # Set the actual value 357 c = self._get(obj) 358 if not c: 359 c = collection(self, obj, self.type) 360 setattr(obj, self._name, c) 361 elif value in c: 362 #log.debug('association: value already in obj: %s' % value) 363 return 364 365 if not from_opposite: 366 undosetassociationaction(self, obj, value) 367 c.items.append(value) 368 369 # Callbacks are only connected if a new relationship has 370 # been established. 371 value.connect('__unlink__', self.__on_unlink, obj) 372 if self.composite: 373 obj.connect('__unlink__', self.__on_composite_unlink, value) 374 375 if not from_opposite and self.opposite: 376 getattr(type(value), self.opposite)._set(value, obj, from_opposite=True, do_notify=do_notify) 377 378 if do_notify: 379 self.notify(obj) 380 381 def _del(self, obj, value, from_opposite=False): 382 # TODO: move code to _del 383 """Delete is used for element deletion and for removal of 384 elements from a list. 385 """ 225 386 #print '__delete__', self, obj, value 226 if self.upper > 1 and not value: 227 raise Exception, 'Can not delete collections' 228 if self.opposite: 229 getattr(self.type, self.opposite)._del(value or self._get(obj), obj) 230 self._del(obj, value or self._get(obj)) 231 232 def _get(self, obj): 233 #print '_get', self, obj 234 # TODO: Handle lower and add items if lower > 0 235 try: 236 return getattr(obj, '_' + self.name) 237 except AttributeError: 387 388 if not value: 238 389 if self.upper > 1: 239 l = Collection(self, obj, self.type) 240 setattr(obj, '_' + self.name, l) 241 return l 242 else: 243 return None 244 245 def _set(self, obj, value): 246 #print '_set', self, obj, value 247 if not isinstance(value, self.type): 248 raise AttributeError, 'Value should be of type %s' % self.type.__name__ 249 self.__set(obj, value) 250 251 def __set(self, obj, value): 252 """Real setter, avoid doing the assertion check twice.""" 390 raise Exception, 'Can not delete collections' 391 value = self._get(obj) 392 if value is None: 393 return 394 395 if not from_opposite: 396 undodelassociationaction(self, obj, value) 397 398 if not from_opposite and self.opposite: 399 getattr(type(value), self.opposite)._del(value, obj, from_opposite=True) 400 253 401 if self.upper > 1: 254 if value in self._get(obj): 255 return 256 self._get(obj).items.append(value) 257 else: 258 setattr(obj, '_' + self.name, value) 259 value.attach(('__unlink__',), self.__on_unlink, obj, value) 402 c = self._get(obj) 403 if c: 404 items = c.items 405 try: 406 items.remove(value) 407 except: 408 pass 409 if not items: 410 delattr(obj, self._name) 411 else: 412 try: 413 delattr(obj, self._name) 414 except: 415 pass 416 #print 'association._del: delattr failed for %s' % self.name 417 418 value.disconnect(self.__on_unlink, obj) 419 if self.composite: 420 obj.disconnect(self.__on_composite_unlink, value) 260 421 self.notify(obj) 261 422 262 def _del(self, obj, value): 263 #print '_del', self, obj, value 264 if self.upper > 1: 265 items = self._get(obj).items 266 items.remove(value) 267 if not items: 268 delattr(obj, '_' + self.name) 269 else: 270 delattr(obj, '_' + self.name) 271 value.detach(self.__on_unlink, obj, value) 272 self.notify(obj) 273 274 def unlink(self, obj): 275 lst = getattr(obj, '_' + self.name) 276 while lst: 277 self.__delete__(obj, lst[0]) 278 279 def __on_unlink(self, name, obj, value): 423 # def unlink(self, obj): 424 # #print 'unlink', self, obj 425 # lst = getattr(obj, self._name) 426 # while lst: 427 # self.__delete__(obj, lst[0]) 428 # # re-establish unlink handler: 429 # value.connect('__unlink__', self.__on_unlink, obj) 430 431 def __on_unlink(self, value, pspec, obj): 280 432 """Disconnect when the element on the other end of the association 281 sends the '__unlink__' signal. This is especially important for282 uni-directional associations.433 (value) sends the '__unlink__' signal. This is especially important 434 for uni-directional associations. 283 435 """ 284 #print '__on_unlink:', self, name, obj, value 285 self.__delete__(obj, value) 436 #print '__on_unlink', name, obj, value 437 if pspec == '__unlink__': 438 self.__delete__(obj, value) 439 # re-establish unlink handler: 440 value.connect('__unlink__', self.__on_unlink, obj) 441 442 def __on_composite_unlink(self, obj, pspec, value): 443 """Unlink value if we have a part-whole (composite) relationship 444 (value is a composite of obj). 445 The implementation of value.unlink() should ensure that no deadlocks 446 occur. 447 """ 448 #print '__on_composite_unlink:', self, name, value 449 if pspec == '__unlink__': 450 value.unlink() 451 obj.connect('__unlink__', self.__on_composite_unlink, value) 452 #else: 453 #print 'RELINK' 454 #value.relink() 286 455 287 456 … … 295 464 296 465 def __init__(self, name, lower, upper, *subsets): 297 self.name = name 466 self.name = intern(name) 467 self._name = intern('_' + name) 298 468 self.lower = lower 299 469 self.upper = upper … … 304 474 305 475 def load(self, obj, value): 306 pass476 raise ValueError, 'Derivedunion: Properties should not be loaded in a derived union %s: %s' % (self.name, value) 307 477 308 478 def save(self, obj, save_func): 309 479 pass 310 480 311 def unlink(self, obj):312 pass481 # def unlink(self, obj): 482 # pass 313 483 314 484 def __str__(self): … … 322 492 u = list() 323 493 for s in self.subsets: 324 #tmp = getattr(obj, s)325 494 tmp = s.__get__(obj) 326 495 if tmp: … … 337 506 return u 338 507 else: 339 assert len(u) <= 1, 'Derived union %s should have length 1 %s' % (self.name, tuple(u))508 assert len(u) <= 1, 'Derived union %s of item %s should have length 1 %s' % (self.name, obj.id, tuple(u)) 340 509 return u and u[0] or None 341 510 … … 345 514 def _del(self, obj, value=None): 346 515 raise AttributeError, 'Can not delete values on a union' 516 347 517 348 518 class redefine(umlproperty): … … 356 526 357 527 def __init__(self, name, type, original): 358 self.name = name 528 self.name = intern(name) 529 self._name = intern('_' + name) 359 530 self.type = type 360 531 self.original = original 361 532 original.add_deriviate(self) 362 533 534 upper = property(lambda s: s.original.upper) 535 lower = property(lambda s: s.original.lower) 536 363 537 def load(self, obj, value): 364 538 if self.original.name == self.name: … … 369 543 self.original.save(obj, save_func) 370 544 371 def unlink(self, obj):372 self.original.unlink(obj)545 # def unlink(self, obj): 546 # self.original.unlink(obj) 373 547 374 548 def __str__(self): 375 549 return '<redefine %s: %s = %s>' % (self.name, self.type.__name__, str(self.original)) 376 550 377 def __get__(self, obj, clazz=None): 551 def __get__(self, obj, class_=None): 552 # No longer needed 378 553 if not obj: 379 554 return self 380 return self.original.__get__(obj, cla zz)555 return self.original.__get__(obj, class_) 381 556 382 557 def __set__(self, obj, value): 558 # No longer needed 383 559 if not isinstance(value, self.type): 384 560 &n
