root/gaphor/tags/gaphor-0.7.0/uml2/element.py

Revision 445, 6.4 kB (checked in by arjanmol, 4 years ago)

*** empty log message ***

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/env python
2 # vim:sw=4:et
3 """Element:
4
5 Method names have changed on some places to emphasize that we deal
6 with a completely new data model here.
7
8 save(save_func)
9 load(name, value)
10 postload()
11     Load/save the element.
12
13 unlink()
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)
22     Connect 'callback' to recieve notifications if one of the properties
23     change (the latter being more memory efficient if you want to listen on
24     more properties). The listener will recieve the property name as
25     first argument, followed by *data.
26
27 disconnect (callback, *data)
28     Disconnect callback as listener.
29
30 notify (name)
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.
35 """
36
37 __all__ = [ 'Element' ]
38
39 import types, mutex
40 import gaphor.misc.uniqueid as uniqueid
41 from properties import umlproperty, association
42
43 class Element(object):
44     """Base class for UML data classes."""
45
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")
55
56     def save(self, save_func):
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):
63                 prop.save(self, save_func)
64
65     def load(self, name, value):
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)
71         except AttributeError, e:
72             raise AttributeError, "'%s' has no property '%s'" % \
73                                         (type(self).__name__, name)
74         else:
75             prop.load(self, value)
76
77     def postload(self):
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()
100
101     def unlink(self):
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))
116         if type(names) is types.StringType:
117             names = (names,)
118         cb = (callback,) + data
119         for name in names:
120             try:
121                 o = self._observers[name]
122                 if not cb in o:
123                     o.append(cb)
124             except KeyError:
125                 # create new entry
126                 self._observers[name] = [cb]
127
128     def disconnect(self, callback, *data):
129         """Detach a callback identified by it's data."""
130         cb = (callback,) + data
131         for values in self._observers.values():
132             # Remove all occurences of 'cb' from values
133             # (if none is found ValueError is raised).
134             try:
135                 while True:
136                     values.remove(cb)
137             except ValueError:
138                 pass
139
140     def notify(self, name, cb_name=None, pspec=None):
141         """Send notification to attached callbacks that a property
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)
158
159     # OCL methods: (from SMW by Ivan Porres (http://www.abo.fi/~iporres/smw))
160
161     def isKindOf(self, class_):
162         """Returns true if the object is an instance of clazz."""
163         return isinstance(self, class_)
164
165     def isTypeOf(self, other):
166         """Returns true if the object is of the same type as other."""
167         return type(self) == type(other)
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)
181
182 if __name__ == '__main__':
183     a = Element()
184     b = Element()
185     def cb_func(name, *args):
186         print '  cb_func:', name, args
187
188     a.connect('ev1', cb_func, a)
189     a.connect('ev1', cb_func, a)
190     a.connect('ev2', cb_func, 'ev2', a)
191
192     print 'notify: ev1'
193     a.notify('ev1')
194     print 'notify: ev2'
195     a.notify('ev2')
196  
197     a.disconnect(cb_func, a)
198
199     print 'notify: ev1'
200     a.notify('ev1')
201     print 'notify: ev2'
202     a.notify('ev2')
203  
Note: See TracBrowser for help on using the browser.