diff --git a/doc/storage.txt b/doc/storage.txt
new file mode 100644
index 000000000..77a427700
--- /dev/null
+++ b/doc/storage.txt
@@ -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'.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gaphor/UML/Element.py b/gaphor/UML/Element.py
index 5b8c8ee57..3c14f0c0a 100644
--- a/gaphor/UML/Element.py
+++ b/gaphor/UML/Element.py
@@ -32,6 +32,14 @@ Geometry = types.ListType
FALSE = 0
TRUE = not FALSE
+elements = { }
+
+def lookup (id):
+ if elements.has_key(id):
+ return elements[id]
+ else:
+ return None
+
class Enumeration_:
'''The Enumerration class is an abstract class that can be used to create
enumerated types. One should inherit from Enumeration and define a variable
@@ -105,7 +113,7 @@ class Element:
attributes and relations are defined by a ._attrdef structure.
A class does not need to define any local variables itself: Element will
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
by the garbage collector. Instead you should call Element::unlink() to
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
_attrdef = { 'presentation': ( Sequence, types.ObjectType ),
'itemsOnUndoStack': (0, types.IntType ) }
- _hash = { }
def __init__(self):
- print "New object of type", self.__class__
+ #print "New object of type", self.__class__
self.__dict__['__id'] = Element._index
self.__dict__['__signals'] = [ ]
#Element._hash[Element._index] = weakref.ref (self)
- Element._hash[Element._index] = self
+ elements[Element._index] = self
Element._index += 1
def unlink(self):
'''Remove all references to the object.'''
- #print 'Element.unlink()'
+ #print 'Element.unlink():', self
self.emit("unlink")
- if Element._hash.has_key(self.__dict__['__id']):
- del Element._hash[self.__dict__['__id']]
+ if elements.has_key(self.__dict__['__id']):
+ del elements[self.__dict__['__id']]
for key in self.__dict__.keys():
# In case of a cyclic reference, we should check if the element
# not yet has been removed.
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):
# Remove each item in the sequence, then remove
# the sequence from __dict__.
- for s in self.__dict__[key].list:
- del self.__dict__[key][s]
+ list = self.__dict__[key].list
+ while len (list) > 0:
+ del self.__dict__[key][list[0]]
del self.__dict__[key]
else:
# do a 'del self.key'
- if isinstance (self.__dict__[key], Element):
- self.__delattr__(key)
+ #if isinstance (self.__dict__[key], Element):
+ #print '\tunlink:', key
+ self.__delattr__(key)
return self
# 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):
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.itemsOnUndoStack -= 1
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:
del self.presentation[presentation]
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):
+ if not self.__dict__.has_key ('itemsOnUndoStack'):
+ return
self.itemsOnUndoStack -= 1
assert 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()
def __get_attr_info(self, key, klass):
- '''Find the record for 'key' in the ._attrdef map.'''
+ '''Find the record for 'key' in the ._attrdef map.'''
done = [ ]
def real_get_attr_info(key, klass):
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)
return self.__dict__[key]
- def __del_seq_item(self, seq, val):
- try:
- index = seq.list.index(val)
- del seq.list[index]
- except ValueError:
- pass
+ #def __del_seq_item(self, seq, val):
+ #try:
+ #index = seq.list.index(val)
+ #del seq.list[index]
+ # seq.list.remove (val)
+ #except ValueError:
+ # pass
def __getattr__(self, key):
- #print 'Element.__getattr__(' + key + ')'
if key == 'id':
return self.__dict__['__id']
elif self.__dict__.has_key(key):
# Key is already in the object
return self.__dict__[key]
else:
+ #if key[0] != '_':
+ #print 'Unknown attr: Element.__getattr__(' + key + ')'
rec = self.__get_attr_info (key, self.__class__)
if rec[0] is Sequence:
# 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 Sequence:
#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):
#print 'del-item rec'
del self.__dict__[key]
if xrec[0] is Sequence:
#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]):
#print 'del-item xrec'
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):
return
xval = self.__dict__[key]
- del self.__dict__[key]
if len(rec) > 2: # Bi-directional relationship
xrec = xval.__get_attr_info (rec[2], rec[1])
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:
del xval.__dict__[rec[2]]
- xval.emit(rec[2])
- self.emit (key)
+ del self.__dict__[key]
+ self.emit (key)
+ xval.emit(rec[2])
def sequence_remove(self, seq, obj):
'''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:
break
#print 'Element.sequence_remove', key
+ seq_len = len (seq)
rec = self.__get_attr_info (key, self.__class__)
if rec[0] is not Sequence:
raise AttributeError, 'Element: This should be called from Sequence'
seq.list.remove(obj)
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:
- 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:
- try:
- del obj.__dict__[rec[2]]
- except Exception, e:
- print 'ERROR: (Element.sequence_remove)', e
-
+ #try:
+ #print 'sequence_remove: item'
+ del obj.__dict__[rec[2]]
+ #except Exception, e:
+ # print 'ERROR: (Element.sequence_remove)', e
+ assert len (seq) == seq_len - 1
# Functions used by the signal functions
def connect (self, 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'])
def emit (self, key):
- if not self.__dict__.has_key ('__signals'):
- print 'No __signals attribute in object', self
- return
+ print 'emit', self, key
+ #if not self.__dict__.has_key ('__signals'):
+ # print 'No __signals attribute in object', self
+ # return
for signal in self.__dict__['__signals']:
signal_func = signal[0]
data = signal[1:]
+ #print 'signal:', signal_func, 'data:', data
signal_func (key, *data)
-#def update_model():
-# '''Do a garbage collection on the hash table, also removing
-# weak references that do not point to valid objects.'''
-# gc.collect()
-# for k in Element._hash.keys():
-# if Element._hash[k]() is None:
-# del Element._hash[k]
+ def save(self, document, parent):
+ def save_children (obj):
+ if isinstance (obj, Element):
+ #subnode = document.createElement (key)
+ subnode = document.createElement ('Reference')
+ node.appendChild (subnode)
+ 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__':
print '\n============== Starting Element tests... ============\n'
@@ -389,7 +477,7 @@ if __name__ == '__main__':
a.unlink()
del a
- assert len (Element._hash) == 0
+ assert len (elements) == 0
print '\tOK ==='
@@ -421,7 +509,7 @@ if __name__ == '__main__':
a.unlink()
- assert len (Element._hash) == 0
+ assert len (elements) == 0
print '\tOK ==='
@@ -468,7 +556,7 @@ if __name__ == '__main__':
a.unlink()
b.unlink()
- assert len (Element._hash) == 0
+ assert len (elements) == 0
print '\tOK ==='
@@ -496,6 +584,18 @@ if __name__ == '__main__':
assert a.ref is 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 = A()
b = A()
@@ -539,8 +639,8 @@ if __name__ == '__main__':
a.unlink()
b.unlink()
- #print Element._hash
- assert len (Element._hash) == 0
+ #print elements
+ assert len (elements) == 0
print '\tOK ==='
@@ -603,7 +703,7 @@ if __name__ == '__main__':
a.unlink()
b.unlink()
- assert len (Element._hash) == 0
+ assert len (elements) == 0
print '\tOK ==='
@@ -660,7 +760,7 @@ if __name__ == '__main__':
b.unlink()
del b
- assert len (Element._hash) == 0
+ assert len (elements) == 0
print '\tOK ==='
@@ -674,14 +774,14 @@ if __name__ == '__main__':
a = A()
b = A()
- assert len (Element._hash) == 2
+ assert len (elements) == 2
a.rel = b
a.unlink()
b.unlink()
- assert len (Element._hash) == 0
+ assert len (elements) == 0
print '\tOK ==='
diff --git a/gaphor/UML/Makefile.am b/gaphor/UML/Makefile.am
index 4f0b478fa..24d370893 100644
--- a/gaphor/UML/Makefile.am
+++ b/gaphor/UML/Makefile.am
@@ -9,4 +9,4 @@ ModelElements.py: $(GEN_UML) $(METAMODEL_XMI)
grep -v "PresentationElement" genModelElements.py > ModelElements.py
rm -f genModelElements.py
-EXTRA_DIST=Element.py ModelElements.py __init__.py
+EXTRA_DIST=Element.py ModelElements.py management.py __init__.py
diff --git a/gaphor/UML/__init__.py b/gaphor/UML/__init__.py
index d20936a6d..715b36299 100644
--- a/gaphor/UML/__init__.py
+++ b/gaphor/UML/__init__.py
@@ -3,6 +3,7 @@
#
from Element import *
from ModelElements import *
+from management import *
Attribute._attrdef['rawAttribute'] = ( '', String )
Operation._attrdef['rawOperation'] = ( '', String )
diff --git a/gaphor/UML/management.py b/gaphor/UML/management.py
new file mode 100644
index 000000000..64548f560
--- /dev/null
+++ b/gaphor/UML/management.py
@@ -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()
diff --git a/gaphor/test-diagram.py b/gaphor/test-diagram.py
index 40b6b34fd..70bd536f0 100755
--- a/gaphor/test-diagram.py
+++ b/gaphor/test-diagram.py
@@ -10,9 +10,12 @@ import diacanvas
import UML
import tree
+uc = getattr (UML, 'UseCase')
+print 'getattr (UML, "UseCase") ->', uc
+
def mainquit(*args):
- for k in UML.Element._hash.keys():
- print "Element", k, ":", UML.Element._hash[k].__dict__
+ for k in UML.elements.keys():
+ print "Element", k, ":", UML.elements[k].__dict__
print "Forcing Garbage collection:"
gtk.main_quit()
@@ -30,21 +33,21 @@ treemodel = tree.NamespaceModel(model)
#item.move (30, 50)
#item = canvas.root.add (diagram.Actor)
#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))
actor = item.get_subject()
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))
comment = item.get_subject()
-del item
+del item#, actor, usecase, comment
-#print "Comment.presentation:", comment.presentation.list
-#print "Actor.presentation:", actor.presentation.list
+print "Comment.presentation:", comment.presentation.list
+print "Actor.presentation:", actor.presentation.list
print "UseCase.presentation:", usecase.presentation.list
#view = diacanvas.CanvasView().set_canvas (dia.canvas)
#display_diagram (dia)
@@ -61,16 +64,15 @@ treemodel.dump()
print 'Going into main'
gtk.main()
+UML.save('x.xml')
+
del win
treemodel.dump()
-#print "Comment.ann.Elem.:", comment.annotatedElement.list
-#print "Actor.comment:", actor.comment.list
-#print "UseCase.comment:", usecase.comment.list
-print "Comment.presentation:", comment.presentation.list
-print "Actor.presentation:", actor.presentation.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..."
dia.unlink()
del dia
@@ -78,17 +80,15 @@ del dia
print "Comment.presentation:", comment.presentation.list
print "Actor.presentation:", actor.presentation.list
print "UseCase.presentation:", usecase.presentation.list
-actor.unlink()
del actor
-usecase.unlink()
del usecase
-comment.unlink()
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()
-for k in UML.Element._hash.keys():
- print "Element", k, ":", UML.Element._hash[k].__dict__
+for k in UML.elements.keys():
+ print "Element", k, ":", UML.elements[k].__dict__
print "Program ended normally..."
diff --git a/tests/diagram-destroy.py b/tests/diagram-destroy.py
new file mode 100644
index 000000000..d6ef8b874
--- /dev/null
+++ b/tests/diagram-destroy.py
@@ -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
+
+
diff --git a/tests/test-treemodel.py b/tests/test-treemodel.py
new file mode 100644
index 000000000..b663ba1fe
--- /dev/null
+++ b/tests/test-treemodel.py
@@ -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()
diff --git a/tests/test-xml.py b/tests/test-xml.py
new file mode 100644
index 000000000..29f0995d9
--- /dev/null
+++ b/tests/test-xml.py
@@ -0,0 +1,8 @@
+#
+# A simple parser
+#
+
+import UML
+
+UML.load ('../gaphor/x.xml')
+UML.save ('aa.xml')