*** empty log message ***
git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@37 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
parent
b28d7bfba8
commit
4b063063d9
50
doc/storage.txt
Normal file
50
doc/storage.txt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
Saving and loading Gaphor diagrams
|
||||||
|
|
||||||
|
The idea is to keep the file format as simple and extensible as possible.
|
||||||
|
|
||||||
|
This is the format as used by Gaphor.
|
||||||
|
|
||||||
|
Everything interesting is between the `Gaphor' start and end tag.
|
||||||
|
|
||||||
|
The file is as flat as possible: UML elements (including Diagram) are at
|
||||||
|
toplevel, no nesting.
|
||||||
|
A UML element can have two tags: `Reference' and `Value'. Reference is used to
|
||||||
|
point to other UML elements, Value has a value inside (an integer or astring).
|
||||||
|
|
||||||
|
Diagram is a special case. Since this element contains a diagram canvas inside,
|
||||||
|
it may become pretty big (with lots of nested elements).
|
||||||
|
This is handled by the load and save function of the Diagram class.
|
||||||
|
All elements inside a canvas have a tag `Item'.
|
||||||
|
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
<Gaphor version="1.0">
|
||||||
|
<Model id="1">
|
||||||
|
<Reference name="ownedElement" refid="2"/>
|
||||||
|
<Reference name="ownedElement" refid="3"/>
|
||||||
|
<Reference name="ownedElement" refid="4"/>
|
||||||
|
</Model>
|
||||||
|
<Diagram id="2">
|
||||||
|
<Reference name="namespace" refid="1"/>
|
||||||
|
<Canvas extents="(9.0, 9.0, 189.0, 247.0)" grid_bg="0xFFFFFFFF"
|
||||||
|
grid_color="0x80ff" grid_int_x="10.0" grid_int_y="10.0"
|
||||||
|
grid_ofs_x="0.0" grid_ofs_y="0.0" snap_to_grid="0"
|
||||||
|
static_extents="0">
|
||||||
|
<CanvasItem affine="(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)" cid="0x8292114"
|
||||||
|
type="CanvasGroup">
|
||||||
|
<CanvasItem affine="(1.0, 0.0, 0.0, 1.0, 150.0, 50.0)" cid="0x8293e74"
|
||||||
|
height="78.0" subject="3" type="Actor" width="38.0"/>
|
||||||
|
<CanvasItem affine="(1.0, 0.0, 0.0, 1.0, 10.0, 10.0)" cid="0x82e7d74"
|
||||||
|
height="26.0" subject="5" type="Comment" width="100.0"/>
|
||||||
|
</CanvasItem>
|
||||||
|
</Canvas>
|
||||||
|
</Diagram>
|
||||||
|
<Actor id="3">
|
||||||
|
<Value name="name">
|
||||||
|
<![CDATA[Actor]]> </Value>
|
||||||
|
<Reference name="namespace" refid="1"/>
|
||||||
|
</Actor>
|
||||||
|
<UseCase id="4">
|
||||||
|
<Reference name="namespace" refid="1"/>
|
||||||
|
</UseCase>
|
||||||
|
<Comment id="5"/>
|
||||||
|
</Gaphor>
|
@ -32,6 +32,14 @@ Geometry = types.ListType
|
|||||||
FALSE = 0
|
FALSE = 0
|
||||||
TRUE = not FALSE
|
TRUE = not FALSE
|
||||||
|
|
||||||
|
elements = { }
|
||||||
|
|
||||||
|
def lookup (id):
|
||||||
|
if elements.has_key(id):
|
||||||
|
return elements[id]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
class Enumeration_:
|
class Enumeration_:
|
||||||
'''The Enumerration class is an abstract class that can be used to create
|
'''The Enumerration class is an abstract class that can be used to create
|
||||||
enumerated types. One should inherit from Enumeration and define a variable
|
enumerated types. One should inherit from Enumeration and define a variable
|
||||||
@ -105,7 +113,7 @@ class Element:
|
|||||||
attributes and relations are defined by a <class>._attrdef structure.
|
attributes and relations are defined by a <class>._attrdef structure.
|
||||||
A class does not need to define any local variables itself: Element will
|
A class does not need to define any local variables itself: Element will
|
||||||
retrieve all information from the _attrdef structure.
|
retrieve all information from the _attrdef structure.
|
||||||
Element._hash is a table containing all assigned objects as weak references.
|
elements is a table containing all assigned objects as weak references.
|
||||||
This class has its own __del__() method. This means that it can not be freed
|
This class has its own __del__() method. This means that it can not be freed
|
||||||
by the garbage collector. Instead you should call Element::unlink() to
|
by the garbage collector. Instead you should call Element::unlink() to
|
||||||
remove all relationships with the element and then it will remove itself if
|
remove all relationships with the element and then it will remove itself if
|
||||||
@ -120,37 +128,39 @@ object if references are lehd by the object on the undo stack.
|
|||||||
_index = 1
|
_index = 1
|
||||||
_attrdef = { 'presentation': ( Sequence, types.ObjectType ),
|
_attrdef = { 'presentation': ( Sequence, types.ObjectType ),
|
||||||
'itemsOnUndoStack': (0, types.IntType ) }
|
'itemsOnUndoStack': (0, types.IntType ) }
|
||||||
_hash = { }
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
print "New object of type", self.__class__
|
#print "New object of type", self.__class__
|
||||||
self.__dict__['__id'] = Element._index
|
self.__dict__['__id'] = Element._index
|
||||||
self.__dict__['__signals'] = [ ]
|
self.__dict__['__signals'] = [ ]
|
||||||
#Element._hash[Element._index] = weakref.ref (self)
|
#Element._hash[Element._index] = weakref.ref (self)
|
||||||
Element._hash[Element._index] = self
|
elements[Element._index] = self
|
||||||
Element._index += 1
|
Element._index += 1
|
||||||
|
|
||||||
def unlink(self):
|
def unlink(self):
|
||||||
'''Remove all references to the object.'''
|
'''Remove all references to the object.'''
|
||||||
#print 'Element.unlink()'
|
#print 'Element.unlink():', self
|
||||||
self.emit("unlink")
|
self.emit("unlink")
|
||||||
if Element._hash.has_key(self.__dict__['__id']):
|
if elements.has_key(self.__dict__['__id']):
|
||||||
del Element._hash[self.__dict__['__id']]
|
del elements[self.__dict__['__id']]
|
||||||
for key in self.__dict__.keys():
|
for key in self.__dict__.keys():
|
||||||
# In case of a cyclic reference, we should check if the element
|
# In case of a cyclic reference, we should check if the element
|
||||||
# not yet has been removed.
|
# not yet has been removed.
|
||||||
if self.__dict__.has_key (key) and \
|
if self.__dict__.has_key (key) and \
|
||||||
key not in ( 'presentation', '__signals', '__id' ):
|
key not in ( 'presentation', 'itemsOnUndoStack', \
|
||||||
|
'__signals', '__id' ):
|
||||||
if isinstance (self.__dict__[key], Sequence):
|
if isinstance (self.__dict__[key], Sequence):
|
||||||
# Remove each item in the sequence, then remove
|
# Remove each item in the sequence, then remove
|
||||||
# the sequence from __dict__.
|
# the sequence from __dict__.
|
||||||
for s in self.__dict__[key].list:
|
list = self.__dict__[key].list
|
||||||
del self.__dict__[key][s]
|
while len (list) > 0:
|
||||||
|
del self.__dict__[key][list[0]]
|
||||||
del self.__dict__[key]
|
del self.__dict__[key]
|
||||||
else:
|
else:
|
||||||
# do a 'del self.key'
|
# do a 'del self.key'
|
||||||
if isinstance (self.__dict__[key], Element):
|
#if isinstance (self.__dict__[key], Element):
|
||||||
self.__delattr__(key)
|
#print '\tunlink:', key
|
||||||
|
self.__delattr__(key)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# Hooks for presentation elements to add themselves:
|
# Hooks for presentation elements to add themselves:
|
||||||
@ -167,6 +177,9 @@ object if references are lehd by the object on the undo stack.
|
|||||||
|
|
||||||
def undo_presentation (self, presentation):
|
def undo_presentation (self, presentation):
|
||||||
if not presentation in self.presentation:
|
if not presentation in self.presentation:
|
||||||
|
# Add myself to the 'elements' hash
|
||||||
|
if len (self.presentation) == 0:
|
||||||
|
elements[self.id] = self
|
||||||
self.presentation = presentation
|
self.presentation = presentation
|
||||||
self.itemsOnUndoStack -= 1
|
self.itemsOnUndoStack -= 1
|
||||||
assert self.itemsOnUndoStack >= 0
|
assert self.itemsOnUndoStack >= 0
|
||||||
@ -175,8 +188,14 @@ object if references are lehd by the object on the undo stack.
|
|||||||
if presentation in self.presentation:
|
if presentation in self.presentation:
|
||||||
del self.presentation[presentation]
|
del self.presentation[presentation]
|
||||||
self.itemsOnUndoStack += 1
|
self.itemsOnUndoStack += 1
|
||||||
|
# Remove yourself from the 'elements' hash
|
||||||
|
if len (self.presentation) == 0:
|
||||||
|
if lookup (self.id):
|
||||||
|
del elements[self.id]
|
||||||
|
|
||||||
def remove_undoability (self):
|
def remove_undoability (self):
|
||||||
|
if not self.__dict__.has_key ('itemsOnUndoStack'):
|
||||||
|
return
|
||||||
self.itemsOnUndoStack -= 1
|
self.itemsOnUndoStack -= 1
|
||||||
assert self.itemsOnUndoStack >= 0
|
assert self.itemsOnUndoStack >= 0
|
||||||
if len (self.presentation) == 0 and self.itemsOnUndoStack == 0:
|
if len (self.presentation) == 0 and self.itemsOnUndoStack == 0:
|
||||||
@ -184,7 +203,7 @@ object if references are lehd by the object on the undo stack.
|
|||||||
self.unlink()
|
self.unlink()
|
||||||
|
|
||||||
def __get_attr_info(self, key, klass):
|
def __get_attr_info(self, key, klass):
|
||||||
'''Find the record for 'key' in the <class>._attrdef map.'''
|
'''Find the record for 'key' in the <klass>._attrdef map.'''
|
||||||
done = [ ]
|
done = [ ]
|
||||||
def real_get_attr_info(key, klass):
|
def real_get_attr_info(key, klass):
|
||||||
if klass in done:
|
if klass in done:
|
||||||
@ -211,21 +230,23 @@ object if references are lehd by the object on the undo stack.
|
|||||||
self.__dict__[key] = Sequence(self, type)
|
self.__dict__[key] = Sequence(self, type)
|
||||||
return self.__dict__[key]
|
return self.__dict__[key]
|
||||||
|
|
||||||
def __del_seq_item(self, seq, val):
|
#def __del_seq_item(self, seq, val):
|
||||||
try:
|
#try:
|
||||||
index = seq.list.index(val)
|
#index = seq.list.index(val)
|
||||||
del seq.list[index]
|
#del seq.list[index]
|
||||||
except ValueError:
|
# seq.list.remove (val)
|
||||||
pass
|
#except ValueError:
|
||||||
|
# pass
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
#print 'Element.__getattr__(' + key + ')'
|
|
||||||
if key == 'id':
|
if key == 'id':
|
||||||
return self.__dict__['__id']
|
return self.__dict__['__id']
|
||||||
elif self.__dict__.has_key(key):
|
elif self.__dict__.has_key(key):
|
||||||
# Key is already in the object
|
# Key is already in the object
|
||||||
return self.__dict__[key]
|
return self.__dict__[key]
|
||||||
else:
|
else:
|
||||||
|
#if key[0] != '_':
|
||||||
|
#print 'Unknown attr: Element.__getattr__(' + key + ')'
|
||||||
rec = self.__get_attr_info (key, self.__class__)
|
rec = self.__get_attr_info (key, self.__class__)
|
||||||
if rec[0] is Sequence:
|
if rec[0] is Sequence:
|
||||||
# We do not have a sequence here... create it and return it.
|
# We do not have a sequence here... create it and return it.
|
||||||
@ -264,13 +285,16 @@ object if references are lehd by the object on the undo stack.
|
|||||||
if rec[0] is not Sequence or xrec[0] is not Sequence:
|
if rec[0] is not Sequence or xrec[0] is not Sequence:
|
||||||
if rec[0] is Sequence:
|
if rec[0] is Sequence:
|
||||||
#print 'del-seq-item rec'
|
#print 'del-seq-item rec'
|
||||||
self.__del_seq_item(self.__dict__[key], xself)
|
#self.__del_seq_item(self.__dict__[key], xself)
|
||||||
|
if xself in self.__dict__[key].list:
|
||||||
|
self.__dict__[key].list.remove(xself)
|
||||||
elif self.__dict__.has_key(key):
|
elif self.__dict__.has_key(key):
|
||||||
#print 'del-item rec'
|
#print 'del-item rec'
|
||||||
del self.__dict__[key]
|
del self.__dict__[key]
|
||||||
if xrec[0] is Sequence:
|
if xrec[0] is Sequence:
|
||||||
#print 'del-seq-item xrec'
|
#print 'del-seq-item xrec'
|
||||||
xself.__del_seq_item(xself.__dict__[rec[2]], self)
|
#xself.__del_seq_item(xself.__dict__[rec[2]], self)
|
||||||
|
xself.__dict__[rec[2]].list.remove (self)
|
||||||
elif xself.__dict__.has_key(rec[2]):
|
elif xself.__dict__.has_key(rec[2]):
|
||||||
#print 'del-item xrec'
|
#print 'del-item xrec'
|
||||||
del xself.__dict__[rec[2]]
|
del xself.__dict__[rec[2]]
|
||||||
@ -297,15 +321,18 @@ object if references are lehd by the object on the undo stack.
|
|||||||
if not self.__dict__.has_key(key):
|
if not self.__dict__.has_key(key):
|
||||||
return
|
return
|
||||||
xval = self.__dict__[key]
|
xval = self.__dict__[key]
|
||||||
del self.__dict__[key]
|
|
||||||
if len(rec) > 2: # Bi-directional relationship
|
if len(rec) > 2: # Bi-directional relationship
|
||||||
xrec = xval.__get_attr_info (rec[2], rec[1])
|
xrec = xval.__get_attr_info (rec[2], rec[1])
|
||||||
if xrec[0] is Sequence:
|
if xrec[0] is Sequence:
|
||||||
xval.__del_seq_item(xval.__dict__[rec[2]], self)
|
#xval.__del_seq_item(xval.__dict__[rec[2]], self)
|
||||||
|
#xval.__dict__[rec[2]].list.remove (self)
|
||||||
|
# Handle it via sequence_remove()
|
||||||
|
del xval.__dict__[rec[2]][self]
|
||||||
else:
|
else:
|
||||||
del xval.__dict__[rec[2]]
|
del xval.__dict__[rec[2]]
|
||||||
xval.emit(rec[2])
|
del self.__dict__[key]
|
||||||
self.emit (key)
|
self.emit (key)
|
||||||
|
xval.emit(rec[2])
|
||||||
|
|
||||||
def sequence_remove(self, seq, obj):
|
def sequence_remove(self, seq, obj):
|
||||||
'''Remove an entry. Should only be called by Sequence's implementation.
|
'''Remove an entry. Should only be called by Sequence's implementation.
|
||||||
@ -315,20 +342,24 @@ object if references are lehd by the object on the undo stack.
|
|||||||
if self.__dict__[key] is seq:
|
if self.__dict__[key] is seq:
|
||||||
break
|
break
|
||||||
#print 'Element.sequence_remove', key
|
#print 'Element.sequence_remove', key
|
||||||
|
seq_len = len (seq)
|
||||||
rec = self.__get_attr_info (key, self.__class__)
|
rec = self.__get_attr_info (key, self.__class__)
|
||||||
if rec[0] is not Sequence:
|
if rec[0] is not Sequence:
|
||||||
raise AttributeError, 'Element: This should be called from Sequence'
|
raise AttributeError, 'Element: This should be called from Sequence'
|
||||||
seq.list.remove(obj)
|
seq.list.remove(obj)
|
||||||
if len(rec) > 2: # Bi-directional relationship
|
if len(rec) > 2: # Bi-directional relationship
|
||||||
xrec = obj.__get_attr_info (rec[2], rec[1])
|
xrec = obj.__get_attr_info (rec[2], obj.__class__) #rec[1])
|
||||||
if xrec[0] is Sequence:
|
if xrec[0] is Sequence:
|
||||||
obj.__del_seq_item(obj.__dict__[rec[2]], self)
|
#obj.__del_seq_item(obj.__dict__[rec[2]], self)
|
||||||
|
#print 'sequence_remove: Sequence'
|
||||||
|
obj.__dict__[rec[2]].list.remove (self)
|
||||||
else:
|
else:
|
||||||
try:
|
#try:
|
||||||
del obj.__dict__[rec[2]]
|
#print 'sequence_remove: item'
|
||||||
except Exception, e:
|
del obj.__dict__[rec[2]]
|
||||||
print 'ERROR: (Element.sequence_remove)', e
|
#except Exception, e:
|
||||||
|
# print 'ERROR: (Element.sequence_remove)', e
|
||||||
|
assert len (seq) == seq_len - 1
|
||||||
# Functions used by the signal functions
|
# Functions used by the signal functions
|
||||||
def connect (self, signal_func, *data):
|
def connect (self, signal_func, *data):
|
||||||
self.__dict__['__signals'].append ((signal_func,) + data)
|
self.__dict__['__signals'].append ((signal_func,) + data)
|
||||||
@ -338,24 +369,81 @@ object if references are lehd by the object on the undo stack.
|
|||||||
self.__dict__['__signals'])
|
self.__dict__['__signals'])
|
||||||
|
|
||||||
def emit (self, key):
|
def emit (self, key):
|
||||||
if not self.__dict__.has_key ('__signals'):
|
print 'emit', self, key
|
||||||
print 'No __signals attribute in object', self
|
#if not self.__dict__.has_key ('__signals'):
|
||||||
return
|
# print 'No __signals attribute in object', self
|
||||||
|
# return
|
||||||
for signal in self.__dict__['__signals']:
|
for signal in self.__dict__['__signals']:
|
||||||
signal_func = signal[0]
|
signal_func = signal[0]
|
||||||
data = signal[1:]
|
data = signal[1:]
|
||||||
|
#print 'signal:', signal_func, 'data:', data
|
||||||
signal_func (key, *data)
|
signal_func (key, *data)
|
||||||
|
|
||||||
#def update_model():
|
def save(self, document, parent):
|
||||||
# '''Do a garbage collection on the hash table, also removing
|
def save_children (obj):
|
||||||
# weak references that do not point to valid objects.'''
|
if isinstance (obj, Element):
|
||||||
# gc.collect()
|
#subnode = document.createElement (key)
|
||||||
# for k in Element._hash.keys():
|
subnode = document.createElement ('Reference')
|
||||||
# if Element._hash[k]() is None:
|
node.appendChild (subnode)
|
||||||
# del Element._hash[k]
|
subnode.setAttribute ('name', key)
|
||||||
|
subnode.setAttribute ('refid', str(obj.__dict__['__id']))
|
||||||
|
elif isinstance (obj, types.IntType) or \
|
||||||
|
isinstance (obj, types.LongType) or \
|
||||||
|
isinstance (obj, types.FloatType):
|
||||||
|
subnode = document.createElement ('Value')
|
||||||
|
node.appendChild (subnode)
|
||||||
|
subnode.setAttribute ('name', key)
|
||||||
|
text = document.createTextNode (str(obj))
|
||||||
|
subnode.appendChild (text)
|
||||||
|
elif isinstance (obj, types.StringType):
|
||||||
|
subnode = document.createElement ('Value')
|
||||||
|
node.appendChild (subnode)
|
||||||
|
subnode.setAttribute ('name', key)
|
||||||
|
cdata = document.createCDATASection (str(obj))
|
||||||
|
subnode.appendChild (cdata)
|
||||||
|
|
||||||
#Element_hash_gc = update_model
|
node = document.createElement (self.__class__.__name__)
|
||||||
|
parent.appendChild (node)
|
||||||
|
node.setAttribute ('id', str (self.__dict__['__id']))
|
||||||
|
for key in self.__dict__.keys():
|
||||||
|
if key not in ( 'presentation', 'itemsOnUndoStack', \
|
||||||
|
'__signals', '__id' ):
|
||||||
|
obj = self.__dict__[key]
|
||||||
|
if isinstance (obj, Sequence):
|
||||||
|
for item in obj.list:
|
||||||
|
save_children (item)
|
||||||
|
else:
|
||||||
|
save_children (obj)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def load(self, node):
|
||||||
|
for child in node.childNodes:
|
||||||
|
if child.tagName == 'Reference':
|
||||||
|
name = child.getAttribute ('name')
|
||||||
|
refid = int (child.getAttribute ('refid'))
|
||||||
|
refelement = lookup (refid)
|
||||||
|
attr_info = self.__get_attr_info (name, self.__class__)
|
||||||
|
if not isinstance (refelement, attr_info[1]):
|
||||||
|
raise ValueError, 'Referenced item is of the wrong type'
|
||||||
|
if attr_info[0] is Sequence:
|
||||||
|
self.__ensure_seq (name, attr_info[1]).list.append (refelement)
|
||||||
|
else:
|
||||||
|
self.__dict__[name] = refelement
|
||||||
|
elif child.tagName == 'Value':
|
||||||
|
name = child.getAttribute ('name')
|
||||||
|
subchild = child.firstChild
|
||||||
|
attr_info = self.__get_attr_info (name, self.__class__)
|
||||||
|
if issubclass (attr_info[1], types.IntType) or \
|
||||||
|
issubclass (attr_info[1], types.LongType):
|
||||||
|
self.__dict__[name] = int (subchild.data)
|
||||||
|
elif issubclass (attr_info[1], types.FloatType):
|
||||||
|
self.__dict__[name] = float (subchild.data)
|
||||||
|
else:
|
||||||
|
self.__dict__[name] = subchild.data
|
||||||
|
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Testing
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
print '\n============== Starting Element tests... ============\n'
|
print '\n============== Starting Element tests... ============\n'
|
||||||
@ -389,7 +477,7 @@ if __name__ == '__main__':
|
|||||||
a.unlink()
|
a.unlink()
|
||||||
del a
|
del a
|
||||||
|
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
@ -421,7 +509,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
a.unlink()
|
a.unlink()
|
||||||
|
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
@ -468,7 +556,7 @@ if __name__ == '__main__':
|
|||||||
a.unlink()
|
a.unlink()
|
||||||
b.unlink()
|
b.unlink()
|
||||||
|
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
@ -496,6 +584,18 @@ if __name__ == '__main__':
|
|||||||
assert a.ref is a
|
assert a.ref is a
|
||||||
assert a.seq.list == [ a ]
|
assert a.seq.list == [ a ]
|
||||||
|
|
||||||
|
b = A()
|
||||||
|
a.seq = b
|
||||||
|
assert b.ref is a
|
||||||
|
assert a.seq.list == [ a, b ]
|
||||||
|
|
||||||
|
|
||||||
|
del a.seq[a]
|
||||||
|
assert a.ref is None
|
||||||
|
assert b.ref is a
|
||||||
|
assert a.seq.list == [ b ]
|
||||||
|
|
||||||
|
b.unlink()
|
||||||
a.unlink()
|
a.unlink()
|
||||||
a = A()
|
a = A()
|
||||||
b = A()
|
b = A()
|
||||||
@ -539,8 +639,8 @@ if __name__ == '__main__':
|
|||||||
a.unlink()
|
a.unlink()
|
||||||
b.unlink()
|
b.unlink()
|
||||||
|
|
||||||
#print Element._hash
|
#print elements
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
@ -603,7 +703,7 @@ if __name__ == '__main__':
|
|||||||
a.unlink()
|
a.unlink()
|
||||||
b.unlink()
|
b.unlink()
|
||||||
|
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
@ -660,7 +760,7 @@ if __name__ == '__main__':
|
|||||||
b.unlink()
|
b.unlink()
|
||||||
del b
|
del b
|
||||||
|
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
@ -674,14 +774,14 @@ if __name__ == '__main__':
|
|||||||
a = A()
|
a = A()
|
||||||
b = A()
|
b = A()
|
||||||
|
|
||||||
assert len (Element._hash) == 2
|
assert len (elements) == 2
|
||||||
|
|
||||||
a.rel = b
|
a.rel = b
|
||||||
|
|
||||||
a.unlink()
|
a.unlink()
|
||||||
b.unlink()
|
b.unlink()
|
||||||
|
|
||||||
assert len (Element._hash) == 0
|
assert len (elements) == 0
|
||||||
|
|
||||||
print '\tOK ==='
|
print '\tOK ==='
|
||||||
|
|
||||||
|
@ -9,4 +9,4 @@ ModelElements.py: $(GEN_UML) $(METAMODEL_XMI)
|
|||||||
grep -v "PresentationElement" genModelElements.py > ModelElements.py
|
grep -v "PresentationElement" genModelElements.py > ModelElements.py
|
||||||
rm -f genModelElements.py
|
rm -f genModelElements.py
|
||||||
|
|
||||||
EXTRA_DIST=Element.py ModelElements.py __init__.py
|
EXTRA_DIST=Element.py ModelElements.py management.py __init__.py
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
from Element import *
|
from Element import *
|
||||||
from ModelElements import *
|
from ModelElements import *
|
||||||
|
from management import *
|
||||||
|
|
||||||
Attribute._attrdef['rawAttribute'] = ( '', String )
|
Attribute._attrdef['rawAttribute'] = ( '', String )
|
||||||
Operation._attrdef['rawOperation'] = ( '', String )
|
Operation._attrdef['rawOperation'] = ( '', String )
|
||||||
|
128
gaphor/UML/management.py
Normal file
128
gaphor/UML/management.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# vim: sw=4
|
||||||
|
import UML, diagram
|
||||||
|
|
||||||
|
from xmllib import XMLParser
|
||||||
|
from xml.dom.minidom import Document
|
||||||
|
|
||||||
|
# Somehow I did not manage it to use the minidom/SAX stuff to create a
|
||||||
|
# decent parser, do I use this depricated (but workin ;) peace of code...
|
||||||
|
# The GaphorParser reads a Gaphor file and creates a DOM representation of
|
||||||
|
# the file.
|
||||||
|
class GaphorParser (XMLParser):
|
||||||
|
'''GaphorParser
|
||||||
|
The GaphorParser examines an inut strream and creates a Document object
|
||||||
|
using the elements found in the XML file. The only restruction that we
|
||||||
|
test is the Gaphor tag and the version (currently only 1.0)'''
|
||||||
|
def __init__ (self, **kw):
|
||||||
|
self.doit = 0
|
||||||
|
self.doc = Document()
|
||||||
|
self.elements = { 'Gaphor': (self.start_Gaphor, self.end_Gaphor) }
|
||||||
|
self.__stack = [ ]
|
||||||
|
apply(XMLParser.__init__, (self,), kw)
|
||||||
|
|
||||||
|
def syntax_error(self, message):
|
||||||
|
raise IOError, "XML document contains syntax errors: " + message
|
||||||
|
|
||||||
|
def start_Gaphor (self, attrs):
|
||||||
|
if attrs['version'] != '1.0':
|
||||||
|
raise Exception, 'Wrong version of Gaphor (' + \
|
||||||
|
attrs['version'] + ')'
|
||||||
|
else:
|
||||||
|
node = self.doc.createElement('Gaphor')
|
||||||
|
self.doc.appendChild (node)
|
||||||
|
self.__stack.append (node)
|
||||||
|
self.doit += 1
|
||||||
|
|
||||||
|
def end_Gaphor (self):
|
||||||
|
self.doit -= 1
|
||||||
|
|
||||||
|
def unknown_starttag(self, tag, attrs):
|
||||||
|
if self.doit:
|
||||||
|
node = self.doc.createElement(tag)
|
||||||
|
self.__stack[-1].appendChild (node)
|
||||||
|
self.__stack.append (node)
|
||||||
|
for key in attrs.keys():
|
||||||
|
node.setAttribute (key, attrs[key])
|
||||||
|
|
||||||
|
def unknown_endtag(self, tag):
|
||||||
|
self.__stack = self.__stack[:-1]
|
||||||
|
|
||||||
|
def handle_cdata(self, tag):
|
||||||
|
cdata = self.doc.createCDATASection (tag)
|
||||||
|
self.__stack[-1].appendChild (cdata)
|
||||||
|
|
||||||
|
def unknown_entityref(self, ref):
|
||||||
|
raise ValueError, tag + " not supported."
|
||||||
|
|
||||||
|
def unknown_charref(self, ref):
|
||||||
|
raise ValueError, tag + " not supported."
|
||||||
|
|
||||||
|
def save (filename=None):
|
||||||
|
document = Document()
|
||||||
|
rootnode = document.createElement ('Gaphor')
|
||||||
|
document.appendChild (rootnode)
|
||||||
|
rootnode.setAttribute ('version', '1.0')
|
||||||
|
for e in UML.elements.values():
|
||||||
|
print 'Saving object', e
|
||||||
|
e.save(document, rootnode)
|
||||||
|
|
||||||
|
if not filename:
|
||||||
|
print document.toxml(indent=' ', newl='\n')
|
||||||
|
else:
|
||||||
|
file = open (filename, 'w')
|
||||||
|
if not file:
|
||||||
|
raise IOError, 'Could not open file `%s\'' % (filename)
|
||||||
|
document.writexml (file, indent='', addindent=' ', newl='\n')
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
def load (filename):
|
||||||
|
'''Load a file and create a model if possible.
|
||||||
|
Exceptions: IOError, ValueError.'''
|
||||||
|
parser = GaphorParser()
|
||||||
|
|
||||||
|
f = open (filename, 'r')
|
||||||
|
while 1:
|
||||||
|
data = f.read(512)
|
||||||
|
parser.feed (data)
|
||||||
|
if len(data) != 512:
|
||||||
|
break;
|
||||||
|
parser.close()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# Now iterate over the tree and create every element in the UML.elements
|
||||||
|
# table.
|
||||||
|
rootnode = parser.doc.firstChild
|
||||||
|
for node in rootnode.childNodes:
|
||||||
|
try:
|
||||||
|
if node.tagName == 'Diagram':
|
||||||
|
cls = getattr (diagram, node.tagName)
|
||||||
|
else:
|
||||||
|
cls = getattr (UML, node.tagName)
|
||||||
|
except:
|
||||||
|
raise ValueError, 'Invalid field in Gaphor file: ' + node.tagName
|
||||||
|
id = int (node.getAttribute('id'))
|
||||||
|
old_index = UML.Element._index
|
||||||
|
UML.Element._index = id
|
||||||
|
cls()
|
||||||
|
if old_index > id:
|
||||||
|
UML.Element._index = old_index
|
||||||
|
#print node.tagName, node.getAttribute('id')
|
||||||
|
|
||||||
|
# Second step: call Element.load() for every object in the element hash.
|
||||||
|
# We also provide the XML node, so it can recreate it's state
|
||||||
|
for node in rootnode.childNodes:
|
||||||
|
id = int (node.getAttribute('id'))
|
||||||
|
element = UML.lookup (id)
|
||||||
|
assert element != None
|
||||||
|
element.load (node)
|
||||||
|
|
||||||
|
|
||||||
|
def flush():
|
||||||
|
'''Flush all elements in the UML.elements'''
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
(key, value) = UML.elements.popitem()
|
||||||
|
except KeyError:
|
||||||
|
break;
|
||||||
|
value.unlink()
|
||||||
|
UML.elements.clear()
|
@ -10,9 +10,12 @@ import diacanvas
|
|||||||
import UML
|
import UML
|
||||||
import tree
|
import tree
|
||||||
|
|
||||||
|
uc = getattr (UML, 'UseCase')
|
||||||
|
print 'getattr (UML, "UseCase") ->', uc
|
||||||
|
|
||||||
def mainquit(*args):
|
def mainquit(*args):
|
||||||
for k in UML.Element._hash.keys():
|
for k in UML.elements.keys():
|
||||||
print "Element", k, ":", UML.Element._hash[k].__dict__
|
print "Element", k, ":", UML.elements[k].__dict__
|
||||||
print "Forcing Garbage collection:"
|
print "Forcing Garbage collection:"
|
||||||
gtk.main_quit()
|
gtk.main_quit()
|
||||||
|
|
||||||
@ -30,21 +33,21 @@ treemodel = tree.NamespaceModel(model)
|
|||||||
#item.move (30, 50)
|
#item.move (30, 50)
|
||||||
#item = canvas.root.add (diagram.Actor)
|
#item = canvas.root.add (diagram.Actor)
|
||||||
#item.move (150, 50)
|
#item.move (150, 50)
|
||||||
|
item = dia.create_item (diagram.UseCase, (50, 150))
|
||||||
|
usecase = item.get_subject()
|
||||||
|
#dia.create_item (diagram.UseCase, (50, 200), subject=usecase)
|
||||||
|
|
||||||
item = dia.create_item (diagram.Actor, (150, 50))
|
item = dia.create_item (diagram.Actor, (150, 50))
|
||||||
actor = item.get_subject()
|
actor = item.get_subject()
|
||||||
actor.name = "Actor"
|
actor.name = "Actor"
|
||||||
|
|
||||||
item = dia.create_item (diagram.UseCase, (50, 150))
|
|
||||||
usecase = item.get_subject()
|
|
||||||
dia.create_item (diagram.UseCase, (50, 200), subject=usecase)
|
|
||||||
|
|
||||||
item = dia.create_item (diagram.Comment, (10,10))
|
item = dia.create_item (diagram.Comment, (10,10))
|
||||||
comment = item.get_subject()
|
comment = item.get_subject()
|
||||||
|
|
||||||
del item
|
del item#, actor, usecase, comment
|
||||||
|
|
||||||
#print "Comment.presentation:", comment.presentation.list
|
print "Comment.presentation:", comment.presentation.list
|
||||||
#print "Actor.presentation:", actor.presentation.list
|
print "Actor.presentation:", actor.presentation.list
|
||||||
print "UseCase.presentation:", usecase.presentation.list
|
print "UseCase.presentation:", usecase.presentation.list
|
||||||
#view = diacanvas.CanvasView().set_canvas (dia.canvas)
|
#view = diacanvas.CanvasView().set_canvas (dia.canvas)
|
||||||
#display_diagram (dia)
|
#display_diagram (dia)
|
||||||
@ -61,16 +64,15 @@ treemodel.dump()
|
|||||||
print 'Going into main'
|
print 'Going into main'
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
|
||||||
|
UML.save('x.xml')
|
||||||
|
|
||||||
del win
|
del win
|
||||||
|
|
||||||
treemodel.dump()
|
treemodel.dump()
|
||||||
|
|
||||||
#print "Comment.ann.Elem.:", comment.annotatedElement.list
|
#print "Comment.presentation:", comment.presentation.list
|
||||||
#print "Actor.comment:", actor.comment.list
|
#print "Actor.presentation:", actor.presentation.list
|
||||||
#print "UseCase.comment:", usecase.comment.list
|
#print "UseCase.presentation:", usecase.presentation.list
|
||||||
print "Comment.presentation:", comment.presentation.list
|
|
||||||
print "Actor.presentation:", actor.presentation.list
|
|
||||||
print "UseCase.presentation:", usecase.presentation.list
|
|
||||||
print "removing diagram..."
|
print "removing diagram..."
|
||||||
dia.unlink()
|
dia.unlink()
|
||||||
del dia
|
del dia
|
||||||
@ -78,17 +80,15 @@ del dia
|
|||||||
print "Comment.presentation:", comment.presentation.list
|
print "Comment.presentation:", comment.presentation.list
|
||||||
print "Actor.presentation:", actor.presentation.list
|
print "Actor.presentation:", actor.presentation.list
|
||||||
print "UseCase.presentation:", usecase.presentation.list
|
print "UseCase.presentation:", usecase.presentation.list
|
||||||
actor.unlink()
|
|
||||||
del actor
|
del actor
|
||||||
usecase.unlink()
|
|
||||||
del usecase
|
del usecase
|
||||||
comment.unlink()
|
|
||||||
del comment
|
del comment
|
||||||
#del dia
|
|
||||||
|
|
||||||
print "Garbage collection after gtk.main() has finished:"
|
UML.flush()
|
||||||
|
|
||||||
|
print "Garbage collection after gtk.main() has finished (should be empty):",
|
||||||
#UML.update_model()
|
#UML.update_model()
|
||||||
for k in UML.Element._hash.keys():
|
for k in UML.elements.keys():
|
||||||
print "Element", k, ":", UML.Element._hash[k].__dict__
|
print "Element", k, ":", UML.elements[k].__dict__
|
||||||
|
|
||||||
print "Program ended normally..."
|
print "Program ended normally..."
|
||||||
|
63
tests/diagram-destroy.py
Normal file
63
tests/diagram-destroy.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#
|
||||||
|
# Test the behavior of a UML tree with a Diagram as leaf. The whole tree
|
||||||
|
# should be freed...
|
||||||
|
#
|
||||||
|
|
||||||
|
import UML
|
||||||
|
import diagram as dia
|
||||||
|
import gc
|
||||||
|
|
||||||
|
model = UML.Model()
|
||||||
|
model.name = "MyModel"
|
||||||
|
|
||||||
|
package = UML.Package()
|
||||||
|
package.name = "Package"
|
||||||
|
|
||||||
|
model.ownedElement = package
|
||||||
|
assert len(model.ownedElement.list) == 1
|
||||||
|
assert model.ownedElement.list[0] is package
|
||||||
|
assert package.namespace is model
|
||||||
|
|
||||||
|
actor = UML.Actor()
|
||||||
|
actor.namespace = package
|
||||||
|
assert len(package.ownedElement.list) == 1
|
||||||
|
assert package.ownedElement.list[0] is actor
|
||||||
|
assert actor.namespace is package
|
||||||
|
|
||||||
|
usecase = UML.UseCase()
|
||||||
|
usecase.namespace = package
|
||||||
|
assert len(package.ownedElement.list) == 2
|
||||||
|
assert package.ownedElement.list[0] is actor
|
||||||
|
assert package.ownedElement.list[1] is usecase
|
||||||
|
assert usecase.namespace is package
|
||||||
|
|
||||||
|
diagram = dia.Diagram()
|
||||||
|
diagram.namespace = package
|
||||||
|
assert len(package.ownedElement.list) == 3
|
||||||
|
assert package.ownedElement.list[0] is actor
|
||||||
|
assert package.ownedElement.list[1] is usecase
|
||||||
|
assert package.ownedElement.list[2] is diagram
|
||||||
|
assert diagram.namespace is package
|
||||||
|
|
||||||
|
diagram.create_item (dia.Actor, pos=(0, 0), subject=actor)
|
||||||
|
diagram.create_item (dia.UseCase, pos=(100, 100), subject=usecase)
|
||||||
|
|
||||||
|
#dia.destroy_diagrams()
|
||||||
|
|
||||||
|
model.unlink()
|
||||||
|
del model
|
||||||
|
usecase.unlink()
|
||||||
|
del usecase
|
||||||
|
actor.unlink()
|
||||||
|
del actor
|
||||||
|
package.unlink()
|
||||||
|
del package
|
||||||
|
diagram.unlink()
|
||||||
|
del diagram
|
||||||
|
|
||||||
|
gc.collect()
|
||||||
|
gc.set_debug (gc.DEBUG_LEAK)
|
||||||
|
|
||||||
|
print "Uncollectable objects found:", gc.garbage
|
||||||
|
|
||||||
|
|
31
tests/test-treemodel.py
Normal file
31
tests/test-treemodel.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import UML, gtk
|
||||||
|
from tree.namespace import *
|
||||||
|
|
||||||
|
import CreateModel
|
||||||
|
|
||||||
|
window = gtk.Window()
|
||||||
|
window.connect('destroy', lambda win: gtk.main_quit())
|
||||||
|
window.set_title('TreeView test')
|
||||||
|
window.set_default_size(250, 400)
|
||||||
|
|
||||||
|
scrolled_window = gtk.ScrolledWindow()
|
||||||
|
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||||
|
window.add(scrolled_window)
|
||||||
|
|
||||||
|
tree_model = NamespaceModel(CreateModel.model)
|
||||||
|
|
||||||
|
tree_view = gtk.TreeView(tree_model)
|
||||||
|
cell = gtk.CellRendererText()
|
||||||
|
# the text in the column comes from column 0
|
||||||
|
column = gtk.TreeViewColumn('', cell, text=0)
|
||||||
|
tree_view.append_column(column)
|
||||||
|
|
||||||
|
scrolled_window.add(tree_view)
|
||||||
|
window.show_all()
|
||||||
|
|
||||||
|
tree_model.dump()
|
||||||
|
|
||||||
|
gtk.main()
|
||||||
|
|
||||||
|
tree_model.dump()
|
8
tests/test-xml.py
Normal file
8
tests/test-xml.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
# A simple parser
|
||||||
|
#
|
||||||
|
|
||||||
|
import UML
|
||||||
|
|
||||||
|
UML.load ('../gaphor/x.xml')
|
||||||
|
UML.save ('aa.xml')
|
Loading…
x
Reference in New Issue
Block a user