root/gaphor/tags/gaphor-0.2.0/gaphor/storage.py

Revision 182, 8.9 kB (checked in by arjanmol, 6 years ago)

*** empty log message ***

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 # vim: sw=4
2 """
3 Load and save Gaphor models to Gaphors own XML format.
4 Three functions are exported:
5 load(filename)
6     load a model from a file
7 save(filename)
8     store the current model in a file
9 verify(filename)
10     check the validity of the file (this does not tell us
11     we have a valid model, just a valid file).
12 """
13 # TODO: use xml.dom.minidom in stead of libxml2 (for compatibility and since libglade uses the python xml stuff too...)
14
15 #import gaphor.UML as UML
16 import UML
17 #import gaphor.diagram as diagram
18 import diagram
19 import diacanvas
20 import os.path
21 import types
22 import xml.dom.minidom as dom
23 import string
24 import gtk
25 import gc
26 import copy
27
28 NS=None
29 ELEMENT='Element'
30 CANVAS='Canvas'
31 CANVAS_ITEM='CanvasItem'
32 VALUE='Value'
33 REFERENCE='Reference'
34 NAME='name'
35 TYPE='type'
36 ID='id'
37 REFID='refid'
38 PVALUE='value'
39
40 def save (filename=None):
41     '''Save the current model to @filename. If no filename is given,
42     standard out is used.'''
43     doc = dom.Document()
44     rootnode = doc.createElement('Gaphor')
45     doc.appendChild (rootnode)
46     rootnode.setAttribute ('version', '1.1')
47     factory = GaphorResource(UML.ElementFactory)
48
49     class Saver:
50         def __init__(self, parent):
51             self.__parent = parent
52
53         def save(self, name, obj):
54             doc = self.__parent.ownerDocument
55             if isinstance (obj, UML.Element):
56                 node = doc.createElement(REFERENCE)
57                 self.__parent.appendChild(node)
58                 node.setAttribute (NAME, name)
59                 #node.setAttribute (REFID, ELEMENT_PREFIX + str(obj.id))
60                 node.setAttribute (REFID, str(obj.id))
61             elif isinstance(obj, diacanvas.Canvas):
62                 node = doc.createElement(CANVAS)
63                 self.__parent.appendChild(node)
64                 obj.save(Saver(node).save)
65             elif isinstance(obj, diacanvas.CanvasItem):
66                 # We can store a reference here, or a subitem
67                 if not name: # subitem
68                     node = doc.createElement(CANVAS_ITEM)
69                     self.__parent.appendChild(node)
70                     node.setAttribute(TYPE, obj.__class__.__name__)
71                     node.setAttribute(ID, str(obj.get_property('id')))
72                     obj.save(Saver(node).save)
73                 else: # reference
74                     node = doc.createElement(REFERENCE)
75                     self.__parent.appendChild(node)
76                     node.setAttribute (NAME, name)
77                     node.setAttribute (REFID, str(obj.get_property('id')))
78             else:
79                 node = doc.createElement(VALUE)
80                 self.__parent.appendChild(node)
81                 node.setAttribute (NAME, name)
82                 if isinstance(obj, types.StringType):
83                     # Ensure that a CDATA section does not contain ']]>', since
84                     # some XML parsers have problems with it...
85                     cdata = doc.createCDATASection(obj.replace(']]>', ']] >'))
86                     node.appendChild(cdata)
87                 else:
88                     node.setAttribute (PVALUE, str(obj))
89
90     for e in factory.values():
91         node = doc.createElement(ELEMENT)
92         rootnode.appendChild(node)
93         node.setAttribute(TYPE, e.__class__.__name__)
94         #node.setAttribute(ID, ELEMENT_PREFIX + str (e.id))
95         node.setAttribute(ID, str (e.id))
96         e.save(Saver(node).save)
97
98     if not filename:
99         print document.toxml(indent='  ', newl='\n')
100     else:
101         file = open (filename, 'w')
102         if not file:
103             raise IOError, 'Could not open file `%s\'' % (filename)
104         try:
105             doc.writexml (file)
106         finally:
107             file.close()
108
109 def _load (doc, factory):
110     '''Load a file and create a model if possible.
111     Exceptions: IOError, ValueError.'''
112
113     version = '1.1'
114     id2element = dict()
115
116     def get_id(node):
117         """ retrieve the ID from a node. in case of old gaphor files,
118         CanvasItem's have a 'cid' in stead of 'id'.
119         """
120         id = node.getAttribute(ID)
121         # Fallback for old gaphor models:
122         if not id and version == '1.0':
123             #log.debug ('Fall back to 1.0 cid property...')
124             id = node.getAttribute('cid')
125         return id
126
127     def load_canvas_items(node, item):
128         for child in node.childNodes:
129             if child.nodeName == CANVAS_ITEM:
130                 type = child.getAttribute(TYPE)
131                 id = get_id(child) #child.getAttribute(ID)
132                 cls = getattr(diagram, type)
133                 child_item = cls()
134                 child_item.set_property('id', copy.copy(id))
135                 #child_item.set_id(id)
136                 #print 'Setting property __id for', child_item, child_item.get_property('id')
137                 id2element[id] = child_item
138                 child_item.set_property('parent', item)
139                 load_canvas_items(child, child_item)
140
141     def load_node(parent, element):
142         """load_node is a recursive function that is used to set values
143         and references from one model element to another.
144         """
145         for node in parent.childNodes:
146             #if node.nodeName not in ( VALUE, REFERENCE, CANVAS, CANVAS_ITEM )
147             if node.nodeName == VALUE:
148                 name = node.getAttribute(NAME)
149                 #print 'value name =', name, element
150                 # If we do not have a value attribute, return the CDATA section
151                 value = node.getAttribute(PVALUE)
152                 if not value:
153                     text_node = node.firstChild
154                     if text_node and text_node.nodeType == dom.Node.TEXT_NODE:
155                         value = text_node.nodeValue
156                     else:
157                         value = ''
158                 #print 'value', element, name, value
159                 element.load(name, value)
160             elif node.nodeName == REFERENCE:
161                 name = node.getAttribute(NAME)
162                 refid = node.getAttribute(REFID)
163                 if id2element.has_key(refid):
164                     value = id2element[refid]
165                 else:
166                     raise ValueError, 'Invalid ID for reference (%s)' % refid
167                 #print 'refer', element, name, value
168                 element.load(name, value)
169             elif node.nodeName == CANVAS:
170                 # For the canvas node, load all sub nodes:
171                 load_node(node, element.canvas)
172             elif node.nodeName == CANVAS_ITEM:
173                 id = get_id(node) #node.getAttribute(ID)
174                 item = id2element[id]
175                 #print 'found item:', item, id
176                 load_node(node, item)
177             else:
178                 raise ValueError, '%s node may only contain Value, Reference, Canvas and CanvasItem nodes' % parent.nodeName
179
180     def postload_node (parent, element):
181         """Call postload() on all created elements. The postload is done
182         in a reverse order (first the child, then the parent items).
183         """
184         for node in parent.childNodes:
185             if node.nodeName == CANVAS:
186                 postload_node(node, element.canvas)
187             elif node.nodeName == CANVAS_ITEM:
188                 id = get_id(node) #node.getAttribute(ID)
189                 item = id2element[id]
190                 postload_node(node, item)
191         element.postload()
192
193     doc.normalize()
194
195     rootnode = doc.documentElement
196     if rootnode.nodeName != 'Gaphor':
197         raise ValueError, 'File %s is not a Gaphor file.' % filename
198
199     version = rootnode.getAttribute('version')
200
201     log.info('0%')
202     # Create Element's
203     for node in rootnode.childNodes:
204         if node.nodeName != ELEMENT:
205             raise ValueError, 'Gaphor node may contain only Element nodes'
206         name = node.getAttribute(TYPE)
207         id = node.getAttribute(ID)
208
209         type = getattr (UML, name)
210         element = factory.create_as (type, id)
211         id2element[id] = element
212         # For a Diagram, create CanvasItem's.
213         if name == 'Diagram':
214             for child in node.childNodes:
215                 if child.nodeName == CANVAS:
216                     assert isinstance (element, UML.Diagram)
217                     load_canvas_items(child, element.canvas.root)
218
219     log.info('0% ... 33%')
220     # Let the elements load their variables
221     for node in rootnode.childNodes:
222         element = factory.lookup (node.getAttribute(ID))
223         load_node(node, element)
224
225     log.info('0% ... 33% ... 66%')
226     # Postprocess the element, give them a chance to clean up etc.
227     diagrams = []
228     for node in rootnode.childNodes:
229         element = factory.lookup (node.getAttribute(ID))
230         element.postload()
231         if isinstance (element, UML.Diagram):
232             diagrams.append((node, element))
233
234     for node, element in diagrams:
235         postload_node(node, element)
236
237     log.info('0% ... 33% ... 66% ... 100%')
238
239 def load (filename):
240     '''Load a file and create a model if possible.
241     Exceptions: GaphorError.'''
242     log.info('Loading file %s' % os.path.basename(filename))
243     try:
244         doc = dom.parse (filename)
245     except Exception, e:
246         log.error('File could no be parsed')
247         raise GaphorError, 'File %s is probably no valid XML.' % filename
248
249     try:
250         # For some reason, loading the model in a temp. factory will
251         # cause DiaCanvas2 to keep a idle handler around... This should
252         # be fixed in DiaCanvas2 ASAP.
253         #factory = UML.ElementFactory()
254         #_load(doc, factory)
255         #gc.collect()
256         #factory.flush()
257         #del factory
258         #gc.collect()
259         #print '===================================== pre load succeeded =='
260         factory = GaphorResource(UML.ElementFactory)
261         factory.flush()
262         gc.collect()
263         _load(doc, factory)
264         # DEBUG code:
265 #       print ''
266 #       print ''
267 #       print ''
268 #       for d in factory.select(lambda e: e.isKindOf(UML.Diagram)):
269 #           for i in d.canvas.root.children:
270 #               print i, i.__dict__
271         gc.collect()
272 #       for d in factory.select(lambda e: e.isKindOf(UML.Diagram)):
273 #           for i in d.canvas.root.children:
274 #               print i, i.__dict__
275 #               print '   ', i.get_property('id')
276
277     except Exception, e:
278         log.info('file %s could not be loaded' % filename)
279         import traceback
280         traceback.print_exc()
281         raise GaphorError, 'Could not load file %s (%s)' % (filename, e)
282
283 def verify (filename):
284     """
285     Try to load the file. If loading succeeded, this file is probably a valid
286     Gaphor file.
287     """
288     try:
289         doc = dom.parse (filename)
290     except Exception, e:
291         log.info('File %s is probably no valid XML.' % filename)
292         return False
293
294     factory = UML.ElementFactory()
295     try:
296         _load(doc, factory)
297     except Exception, e:
298         log.info('File %s could not be loaded' % filename)
299         return False
300
301     return True
Note: See TracBrowser for help on using the browser.