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

Revision 465, 12.3 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 # vim: sw=4:et
2 """Load and save Gaphor models to Gaphors own XML format.
3
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
14 from __future__ import generators
15
16 from cStringIO import StringIO
17 from xml.sax.saxutils import escape
18 import types
19 import sys
20 import os.path
21 import gc
22 import UML
23 import parser
24 import diagram
25 import diacanvas
26 import gaphor
27 from gaphor.i18n import _
28 #from gaphor.misc.xmlwriter import XMLWriter
29
30 __all__ = [ 'load', 'save' ]
31
32 FILE_FORMAT_VERSION = '3.0'
33
34 def save(writer=None, factory=None, status_queue=None):
35     for status in save_generator(writer, factory):
36         if status_queue:
37             status_queue(status)
38
39 def save_generator(writer=None, factory=None):
40     """Save the current model using @writer, which is a
41     gaphor.misc.xmlwriter.XMLWriter instance (or at least a SAX serializer
42     with CDATA support).
43     """
44     # Make bool work for Python 2.2
45     bool_ = type(bool(0))
46
47     def save_reference(name, value):
48         """Save a value as a reference to another element in the model.
49         This applies to both UML as well as canvas items.
50         """
51         # Save a reference to the object:
52         if value.id: #, 'Referenced element %s has no id' % value
53             writer.startElement(name, {})
54             writer.startElement('ref', { 'refid': value.id })
55             writer.endElement('ref')
56             writer.endElement(name)
57
58     def save_collection(name, value):
59         """Save a list of references.
60         """
61         if len(value) > 0:
62             writer.startElement(name, {})
63             writer.startElement('reflist', {})
64             for v in value:
65                 #save_reference(name, v)
66                 if v.id: #, 'Referenced element %s has no id' % v
67                     writer.startElement('ref', { 'refid': v.id })
68                     writer.endElement('ref')
69             writer.endElement('reflist')
70             writer.endElement(name)
71
72     def save_value(name, value):
73         """Save a value (attribute).
74         If the value is a string, it is saves as a CDATA block.
75         """
76         if value is not None:
77             writer.startElement(name, {})
78             writer.startElement('val', {})
79             if isinstance(value, types.StringTypes):
80                 writer.startCDATA()
81                 writer.characters(value)
82                 writer.endCDATA()
83             elif isinstance(value, bool_):
84                 # Write booleans as 0/1.
85                 writer.characters(str(int(value)))
86             else:
87                 writer.characters(str(value))
88             writer.endElement('val')
89             writer.endElement(name)
90
91     def save_element(name, value):
92         """Save attributes and references from items in the gaphor.UML module.
93         A value may be a primitive (string, int), a gaphor.UML.collection
94         (which contains a list of references to other UML elements) or a
95         diacanvas.Canvas (which contains canvas items).
96         """
97         if isinstance (value, (UML.Element, diacanvas.CanvasItem)):
98             save_reference(name, value)
99         elif isinstance(value, UML.collection):
100             save_collection(name, value)
101         elif isinstance(value, diacanvas.Canvas):
102             writer.startElement('canvas', {})
103             value.save(save_canvasitem)
104             writer.endElement('canvas')
105         else:
106             save_value(name, value)
107
108     def save_canvasitem(name, value, reference=False):
109         """Save attributes and references in a gaphor.diagram.* object.
110         The extra attribute reference can be used to force UML
111         """
112         #log.debug('saving canvasitem: %s|%s %s' % (name, value, type(value)))
113         if reference or isinstance(value, UML.Element):
114             save_reference(name, value)
115         elif isinstance(value, UML.collection):
116             save_collection(name, value)
117         elif isinstance(value, diacanvas.CanvasItem):
118             writer.startElement('item', { 'id': value.id,
119                                           'type': value.__class__.__name__ })
120             value.save(save_canvasitem)
121             writer.endElement('item')
122         else:
123             save_value(name, value)
124
125     if not factory:
126         factory = gaphor.resource(UML.ElementFactory)
127
128     writer.startDocument()
129     writer.startElement('gaphor', { 'version': FILE_FORMAT_VERSION,
130                                     'gaphor-version': gaphor.resource('Version') })
131
132     size = factory.size()
133     n = 0
134     for e in factory.values():
135         clazz = e.__class__.__name__
136         assert e.id
137         writer.startElement(clazz, { 'id': str(e.id) })
138         e.save(save_element)
139         writer.endElement(clazz)
140         n += 1
141         if n % 25 == 0:
142             yield (n * 100) / size
143
144     writer.endElement('gaphor')
145     writer.endDocument()
146
147
148 def load_elements(elements, factory, status_queue=None):
149     for status in load_elements_generator(elements, factory):
150         if status_queue:
151             status_queue(status)
152
153 def load_elements_generator(elements, factory, gaphor_version=None):
154     """Load a file and create a model if possible.
155     Exceptions: IOError, ValueError.
156     """
157
158     log.debug(_('Loading %d elements...') % len(elements))
159
160     # The elements are iterated three times:
161     size = len(elements) * 3
162     def update_status_queue(_n=[0]):
163         n = _n[0] = _n[0] + 1
164         if n % 10 == 0:
165             return (n * 100) / size
166
167     #log.info('0%')
168
169     # Fix version inconsistencies
170     version_0_6_2(elements, factory, gaphor_version)
171
172     # First create elements and canvas items in the factory
173     # The elements are stored as attribute 'element' on the parser objects:
174     for id, elem in elements.items():
175         yield update_status_queue()
176         if isinstance(elem, parser.element):
177             try:
178                 cls = getattr(UML, elem.type)
179                 #log.debug('Creating UML element for %s' % elem)
180                 elem.element = factory.create_as(cls, id)
181             except:
182                 raise
183         elif isinstance(elem, parser.canvasitem):
184             cls = getattr(diagram, elem.type)
185             #log.debug('Creating canvas item for %s' % elem)
186             elem.element = diagram.create_as(cls, id)
187         else:
188             raise ValueError, 'Item with id "%s" and type %s can not be instantiated' % (id, type(elem))
189
190     #log.info('0% ... 33%')
191
192     # load attributes and create references:
193     for id, elem in elements.items():
194         yield update_status_queue()
195         # Ensure that all elements have their element instance ready...
196         assert hasattr(elem, 'element')
197
198         # establish parent/child relations on canvas items:
199         if isinstance(elem, parser.element) and elem.canvas:
200             for item in elem.canvas.canvasitems:
201                 assert item in elements.values(), 'Item %s (%s) is a canvas item, but it is not in the parsed objects table' % (item, item.id)
202                 item.element.set_property('parent', elem.element.canvas.root)
203         if isinstance(elem, parser.canvasitem):
204             for item in elem.canvasitems:
205                 assert item in elements.values(), 'Item %s (%s) is a canvas item, but it is not in the parsed objects table' % (item, item.id)
206                 item.element.set_property('parent', elem.element)
207
208         # load attributes and references:
209         for name, value in elem.values.items():
210             try:
211                 elem.element.load(name, value)
212             except:
213                 log.error('Loading value %s (%s) for element %s failed.' % (name, value, elem.element))
214                 raise
215
216         for name, refids in elem.references.items():
217             if type(refids) == type([]):
218                 for refid in refids:
219                     try:
220                         ref = elements[refid]
221                     except:
222                         raise ValueError, 'Invalid ID for reference (%s) for element %s.%s' % (refid, elem.type, name)
223                     else:
224                         try:
225                             elem.element.load(name, ref.element)
226                         except:
227                             log.error('Loading %s.%s with value %s failed' % (type(elem.element).__name__, name, ref.element.id))
228                             raise
229             else:
230                 try:
231                     ref = elements[refids]
232                 except:
233                     raise ValueError, 'Invalid ID for reference (%s)' % refids
234                 else:
235                     try:
236                         elem.element.load(name, ref.element)
237                     except:
238                         log.error('Loading %s.%s with value %s failed' % (type(elem.element).__name__, name, ref.element.id))
239                         raise
240
241     # Fix version inconsistencies
242     version_0_5_2(elements, factory, gaphor_version)
243
244     # do a postload:
245     for id, elem in elements.items():
246         yield update_status_queue()
247         elem.element.postload()
248
249     factory.notify_model()
250
251
252 def load(filename, factory=None, status_queue=None):
253     """Load a file and create a model if possible.
254     Optionally, a status queue function can be given, to which the
255     progress is written (as status_queue(progress)).
256     Exceptions: GaphorError.
257     """
258     for status in load_generator(filename, factory):
259         if status_queue:
260             status_queue(status)
261
262 def load_generator(filename, factory=None):
263     """Load a file and create a model if possible.
264     This function is a generator. It will yield values from 0 to 100 (%)
265     to indicate its progression.
266
267     Exceptions: GaphorError.
268     """
269     log.info('Loading file %s' % os.path.basename(filename))
270     try:
271         # Use the incremental parser and yield the percentage of the file.
272         loader = parser.GaphorLoader()
273         for percentage in parser.parse_generator(filename, loader):
274             if percentage:
275                 yield percentage / 2
276             else:
277                 yield percentage
278         elements = loader.elements
279         gaphor_version = loader.gaphor_version
280         #elements = parser.parse(filename)
281         #yield 100
282     except Exception, e:
283         log.error('File could no be parsed', e)
284         raise
285
286     try:
287         if not factory:
288             factory = gaphor.resource(UML.ElementFactory)
289         factory.flush()
290         gc.collect()
291         for percentage in load_elements_generator(elements, factory, gaphor_version):
292             if percentage:
293                 yield percentage / 2 + 50
294             else:
295                 yield percentage
296         gc.collect()
297     except Exception, e:
298         log.info('file %s could not be loaded' % filename, e)
299         import traceback
300         traceback.print_exc()
301         raise
302
303
304 # Version inconsistencies can be fixed:
305
306 def version_0_6_2(elements, factory, gaphor_version):
307     """Before 0.6.2 an Interface could be represented by a ClassItem and
308     a InterfaceItem. Now only InterfaceItems are used.
309     """
310     if tuple(map(int, gaphor_version.split('.'))) < (0, 6, 2):
311         for elem in elements.values():
312             try:
313                 if type(elem) is parser.element and elem.type == 'Interface':
314                     for p_id in elem.presentation:
315                         p = elements[p_id]
316                         if p.type == 'ClassItem':
317                             p.type = 'InterfaceItem'
318                             p.values['drawing-style'] = '0'
319                         elif p.type == 'InterfaceItem':
320                             p.values['drawing-style'] = '2'
321             except Exception, e:
322                 log.error('Error while updating InterfaceItems', e)
323
324
325 def version_0_5_2(elements, factory, gaphor_version):
326     """Before version 0.5.2, the wrong memberEnd of the association was
327     holding the aggregation information.
328     """
329     if tuple(map(int, gaphor_version.split('.'))) < (0, 5, 2):
330         log.info('Fix composition on Associations (file version: %s)' % gaphor_version)
331         for elem in elements.values():
332             try:
333                 if elem.type == 'Association':
334                     a = elem.element
335                     agg1 = a.memberEnd[0].aggregation
336                     agg2 = a.memberEnd[1].aggregation
337                     a.memberEnd[0].aggregation = agg2
338                     a.memberEnd[1].aggregation = agg1
339             except Exception, e:
340                 log.error('Error while updating Association', e)
341
Note: See TracBrowser for help on using the browser.