root/gaphor/tags/gaphor-0.7.0/utils/genUML.py

Revision 189, 20.2 kB (checked in by arjanmol, 6 years ago)

*** empty log message ***

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/env python
2 # vim: sw=4
3 # MetaModel Parser: Parse the UML metamodel definition version 1.3 file
4 # as defined by the OMG and create python code to be used by Gaphor.
5 #
6 # Note that the output is written to STDOUT and the status messages and errors
7 # are written to STDERR.
8 #
9 # - Although References are stored, nothing is done with them, since they
10 #   we also have AssociationEnds, which contain the same information and more.
11 # - Note that this model is just a start. In the end the data model should be
12 #   narrowed to the model defined in chapter 5 of the OMG UML 1.4 specs
13 #   (Model Interchange Using XMI).
14 # - Once Gaphor is mature enough, the data model will be drawn in Gaphor
15 #   (for documentation and demonstration purposes) and Gaphor will provide
16 #   its own data model.
17 #
18 import sys, getopt
19 from xmllib import *
20 import types
21 import traceback
22
23 msg = sys.stderr.write
24
25 # Used to map DataTypes to Python primitives:
26 primitives = {
27     'Geometry': 'list',
28     'LocationReference': 'str',
29     'Boolean': 'int',
30     'String':'str',
31     'UnlimitedInteger': 'long',
32     'Integer': 'int',
33     'Name': 'str'
34 }
35
36 # For those classes no class definition is generated:
37 custom_elements = [
38     'Element',
39     'PresentationElement', # ModelElement.presentation is set to not navigable
40     'Multiplicity',
41     'MultiplicityRange'
42 ]
43
44 default_values = {
45     'isNavigable': 'True',
46     'Name': '\'\'',
47     'Boolean': 'False',
48     'VisibilityKind': 'VK_PUBLIC',
49     'OrderingKind': 'OK_UNORDERED',
50     'ChangeableKind': 'CK_CHANGEABLE',
51     'ScopeKind': 'SK_INSTANCE',
52     'PseudostateKind': 'PK_CHOICE',
53     'ParameterDirectionKind': 'PDK_IN',
54     'AggregationKind': 'AK_NONE',
55     'CallConcurrencyKind': 'CCK_SEQUENTIAL',
56     'Expression': 'None',
57     'ActionExpression': 'None',
58     'IterationExpression': 'None',
59     'MappingExpression': 'None',
60     'BooleanExpression': 'None',
61     'TimeExpression': 'None',
62     'TypeExpression': 'None',
63     'ObjectSetExpression': 'None',
64     'ProcedureExpression': 'None',
65     'ArgListsExpression': 'None',
66     'Multiplicity': '\'\'', # Defaults to String
67     'Integer': '1', # Defaults to Int
68     'UnlimitedInteger': '1', # Defaults to Long
69     'String': '\'\'', # Defaults to String
70     'Geometry': 'None', # Defaults to a list (of length 4)
71     'LocationReference': 'None'
72 }
73
74 translations = {
75     'ActivityGraph.partition': (lambda a: a, 'isNavigable', False),
76     'Classifier.collaboration': (lambda a: a, 'name', 'classifierCollaboration'),
77     'Classifier.classifierRole': (lambda a: a, 'name', 'classifierBaseRole'),
78     'Collaboration.collaboration': (lambda a: a, 'isNavigable', False),
79     'Collaboration.participatingLink': (lambda a: a, 'isNavigable', False),
80     'CollaborationInstanceSet.collaboration': (lambda a: a, 'isNavigable', False),
81     'Feature.classifierRole': (lambda a: a.other_end(), 'isNavigable', False),
82     'Instance.collaborationInstanceSet': (lambda a: a, 'isNavigable', False),
83     'Link.owner': (lambda a: a, 'name', 'linkOwner'),
84     'Link.playedRole': (lambda a: a, 'name', 'playedAssociationRole'),
85     'Link.collaborationInstanceSet': (lambda a: a, 'isNavigable', False),
86     'Message.sender': (lambda a: a.other_end(), 'isNavigable', False),
87     'Message.receiver': (lambda a: a.other_end(), 'isNavigable', False),
88     'ModelElement.collaborationInstanceSet': (lambda a: a, 'isNavigable', False),
89     'ModelElement.presentation': (lambda a: a, 'isNavigable', False),
90     'Operation.collaboration': (lambda a: a, 'name', 'operationCollaboration'),
91     'Package.elementImport': (lambda a: a, 'name', 'packageImport'),
92     'State.entry': (lambda a: a.other_end(), 'name', 'entryState'),
93     'State.exit': (lambda a: a.other_end(), 'name', 'exitState'),
94     'State.doActivity': (lambda a: a.other_end(), 'name', 'doActivityState'),
95     'Stimulus.argument': (lambda a: a.other_end(), 'name', 'argumentStimulus'),
96     'Stimulus.sender': (lambda a: a.other_end(), 'name', 'senderStimulus'),
97     'Stimulus.receiver': (lambda a: a.other_end(), 'name', 'receiverStimulus'),
98 }
99
100 # An overall dictionary where all elements will be indexed by their id
101 elements = { }
102
103 # Data classes
104
105 class ModelElement:
106     def __init__(self, id, name):
107         global elements
108         self.id = id
109         self.name = name
110         elements[id] = self
111    
112     def resolve_references(self):
113         pass
114
115 class Package(ModelElement):
116     def __init__(self, id, name):
117         ModelElement.__init__(self, id, name)
118         self.imports = [ ]
119         self.datatypes = [ ]
120         self.classes = [ ]
121         self.associations = [ ]
122        
123     def add_import(self, i):
124         self.imports.append(i)
125        
126     def add_datatype(self, d):
127         self.datatypes.append(d)
128        
129     def add_class(self, c):
130         self.classes.append(c)
131        
132     def add_association(self, a):
133         self.associations.append(a)
134
135 class Import(ModelElement):
136     def __init__(self, id, name, importedNamespace):
137         ModelElement.__init__(self, id, name)
138         self.importedNamespace = importedNamespace
139
140     def resolve_references(self):
141         global elements
142         self.importedNamespace = elements[self.importedNamespace]
143
144     def str(self):
145         return 'from %s import *\n' % self.importedNamespace.name
146
147 class Class(ModelElement):
148     def __init__(self, id, name, isAbstract, supertypes):
149         ModelElement.__init__(self, id, name)
150         self.isAbstract = isAbstract == 'true'
151         self.supertypes = supertypes and supertypes.split(' ') or None
152         self.attributes = [ ]
153         self.references = [ ]
154         self.associationEnds = [ ]
155
156     def add_attribute(self, a):
157         self.attributes.append(a)
158
159     def add_reference(self, r):
160         self.references.append(r)
161
162     def add_association_end(self, ae):
163         self.associationEnds.append(ae)
164
165     def is_referenced(self, ae):
166         for r in self.references:
167             if r.referencedEnd is ae:
168                 return True
169         return False
170
171     def resolve_references(self):
172         global elements
173         if self.supertypes:
174             n = [ ]
175             for id in self.supertypes:
176                 n.append(elements[id])
177             self.supertypes = n
178        
179     def doimpl(self, done):
180         "Create a string containing all attributes and associations."
181         if self in done:
182             return ''
183         s = ''
184         for t in self.supertypes or ():
185             s += t.doimpl(done)
186         if self.attributes or self.references:
187             s += '    # from %s:\n' % self.name
188         # Build attributes: 'name': (default value, type)
189         for a in self.attributes:
190             s += '    ' + a.str() + ',\n'
191         # Build associations: 'name': (default or 'Sequence', type[, other_end])
192         #for r in self.references:
193         #    s += '    ' + r.str() + ',\n'
194         for e in self.associationEnds:
195             if e.str() != '':
196                 s += '    ' + e.str() + ',\n'
197         done.append(self)
198         return s
199
200     def defstr(self, done):
201         """Return the class definition. the list done is used to create
202         definitions of supertypes before the actual class."""
203         if self in done:
204             return ''
205         s = ''
206         for t in self.supertypes or ():
207             s += t.defstr(done)
208         s += 'class %s(' % self.name
209         if self.supertypes:
210             for t in self.supertypes:
211                 s += '%s,' % t.name
212         else:
213             s += 'Element,'
214         s = s[:-1] + '): __abstract__ = %s\n' % (self.isAbstract and 'True' or 'False')
215         done.append(self)
216         return s
217
218     def implstr(self):
219         "Return the dict structure here"
220         if self.isAbstract:
221             return ''
222         s = '%s.__attributes__ = {\n' % self.name
223         s += self.doimpl([ ])
224         s = s[:-2] + '\n}\n\n'
225         return s
226
227 class DataType(ModelElement):
228     def __init__(self, id, name):
229         ModelElement.__init__(self, id, name)
230         self.isEnumeration = 0
231         self.enumvalues = [ ]
232
233     def is_enumeration(self):
234         self.isEnumeration = 1
235
236     def add_enumvalue(self, val):
237         self.enumvalues.append(val)
238
239     def str(self):
240         s = ''
241         if self.isEnumeration:
242             for e in self.enumvalues:
243                 s = s + "%s = '%s'\n" % (e.upper(), e)
244             s += 'class %s(Enum):\n' % self.name
245             s += '    _values = ['
246             for e in self.enumvalues:
247                 s += ' %s,' % e.upper()
248             s = s[:-1] + ' ]\n\n'
249         else:
250             s = 'class %s(%s): pass\n\n' % (self.name, primitives[self.name])
251         return s
252
253 class Attribute(ModelElement):
254     def __init__(self, id, name, type):
255         ModelElement.__init__(self, id, name)
256         self.type = type
257         self.multiplicity = None
258
259     def set_multiplicity(self, m):
260         self.multiplicity = m
261
262     def resolve_references(self):
263         global elements
264         self.type = elements[self.type]
265
266     def str(self):
267         s = "'%s': ( " % self.name
268         if default_values.has_key(self.name):
269             s += '%s' % default_values[self.name]
270         else:
271             try:
272                 s += '%s' % default_values[self.type.name]
273             except KeyError, e:
274                 msg('No default value found for type %s' % self.type.name)
275                 s += 'None'
276         s += ', %s )' % self.type.name
277         return s
278
279 class Reference(Attribute):
280     def __init__(self, id, name, type, referencedEnd):
281         Attribute.__init__(self, id, name, type)
282         self.referencedEnd = referencedEnd
283
284     def resolve_references(self):
285         Attribute.resolve_references(self)
286         global elements
287         self.referencedEnd = elements[self.referencedEnd]
288         # Set it navigable:
289         #self.referencedEnd.isNavigable = True
290
291     def str(self):
292         return self.referencedEnd.str()[:-4]
293
294 class Association(ModelElement):
295     def __init__(self, id, name):
296         ModelElement.__init__(self, id, name)
297         self.associationEnds = [ ]
298
299     def add_association_end(self, ae):
300         self.associationEnds.append(ae)
301
302     def other_end(self, ae):
303         if ae is self.associationEnds[0]:
304             return self.associationEnds[1]
305         else:
306             return self.associationEnds[0]
307
308 class AssociationEnd(ModelElement):
309     def __init__(self, id, name, type, association, isNavigable):
310         ModelElement.__init__(self, id, name)
311         self.type = type
312         self.isNavigable = isNavigable == 'true'
313         self.multiplicity = None #Multiplicity(1, 1, 'false', 'false')
314         self.association = association
315
316     def set_multiplicity(self, m):
317         self.multiplicity = m
318
319     def resolve_references(self):
320         global elements
321         self.type = elements[self.type]
322
323     def other_end(self):
324         return self.association.other_end(self)
325
326     def str(self):
327         if not self.isNavigable:
328             return ''
329
330         s = "'%s': ( %s, %s" % (self.name, self.multiplicity.str(), self.type.name)
331         # Get the association end on the other end of the association:
332         e = self.other_end()
333         # Add reverse entry if the other end is navigable too:
334         if e.isNavigable:
335             s += ", '%s'" % e.name
336         s += ' )'
337         return s
338
339 class Multiplicity:
340     def __init__(self, lower, upper, isOrdered, isUnique):
341         self.lower = lower
342         self.upper = upper
343         self.isOrdered = isOrdered == 'true'
344         self.isUnique = isUnique == 'true'
345
346     def str(self):
347         if self.upper == -1:
348             return 'Sequence'
349         return 'None'
350
351 # UML 1.3 namespace (don't forget the trailing space):
352 NS='omg.org/mof.Model/1.3 '
353
354 # This is the parser. It's a bit hackish and maybe it only works for the
355 # UML 1.3 MetaModel (which is enough actually). The parser is stack based:
356 # for packages, classes, datatypes and associations new maps are created.
357 class MetaModelParser(XMLParser):
358
359     def __init__(self, **kw):
360         self.mmstack = [ ]
361         self.xmifield = None
362         # These are the elements we can expect in the MetaModel:
363         self.elements = {
364             'XMI': (self.start_XMI, self.end_XMI),
365             'XMI.CorbaTcAlias': (self.start_XMI, self.end_XMI),
366             'XMI.CorbaTcBoolean': (self.start_XMI, self.end_XMI),
367             'XMI.CorbaTcEnum': (self.start_XMICorbaTcEnum, self.end_XMICorbaTcEnum),
368             'XMI.CorbaTcEnumLabel': (self.start_XMICorbaTcEnumLabel, self.end_XMICorbaTcEnumLabel),
369             'XMI.CorbaTcLong': (self.start_XMI, self.end_XMI),
370             'XMI.CorbaTcString': (self.start_XMI, self.end_XMI),
371             'XMI.CorbaTypeCode': (self.start_XMI, self.end_XMI),
372             'XMI.any': (self.start_XMI, self.end_XMI),
373             'XMI.content': (self.start_XMI, self.end_XMI),
374             'XMI.field': (self.start_XMIfield, self.end_XMIfield),
375             'XMI.header': (self.start_XMI, self.end_XMI),
376             'XMI.metamodel': (self.start_XMI, self.end_XMI),
377             'XMI.model': (self.start_XMI, self.end_XMI),
378             NS + 'Association': (self.start_Association, self.end_Association),
379             NS + 'AssociationEnd': (self.start_AssociationEnd, self.end_AssociationEnd),
380             NS + 'AssociationEnd.multiplicity': (self.start_multiplicity, self.end_multiplicity),
381             NS + 'Attribute': (self.start_Attribute, self.end_Attribute),
382             NS + 'Class': (self.start_Class, self.end_Class),
383             NS + 'DataType': (self.start_DataType, self.end_DataType),
384             NS + 'DataType.typeCode': (self.start_DataTypetypeCode, self.end_DataTypetypeCode),
385             NS + 'Import': (self.start_Import, self.end_Import),
386             NS + 'Namespace.contents': (self.start_Namespacecontents, self.end_Namespacecontents),
387             NS + 'Package': (self.start_Package, self.end_Package),
388             NS + 'Reference': (self.start_Reference, self.end_Reference),
389             NS + 'StructuralFeature.multiplicity': (self.start_multiplicity, self.end_multiplicity),
390             NS + 'Tag': (self.start_Tag, self.end_Tag),
391             NS + 'Tag.values': (self.start_Tagvalues, self.end_Tagvalues)
392         }
393         apply(XMLParser.__init__, (self,), kw)
394
395     def push(self, val):
396         self.mmstack.insert(0, val)
397
398     def pop(self):
399         val = self.mmstack[0]
400         self.mmstack.remove(val)
401         return val
402
403     def peek(self):
404         return self.mmstack[0]
405
406     def handle_xml(self, encoding, standalone):
407         pass
408
409     def handle_doctype(self, tag, pubid, syslit, data):
410         pass
411
412     def start_XMI(self, data):
413         pass
414         # Skip XMI related stuff, not interesting
415
416     def end_XMI(self):
417         pass
418
419     def start_Association(self, data):
420         assert isinstance(self.peek(), Package)
421         a = Association(id=data[NS + 'xmi.id'], name=data[NS + 'name'])
422         self.peek().add_association(a)
423         self.push(a)
424         msg('a')
425
426     def end_Association(self):
427         self.pop()
428
429     def start_AssociationEnd(self, data):
430         assert isinstance(self.peek(), Association)
431         ae = AssociationEnd(id=data[NS + 'xmi.id'],
432                             name=data[NS + 'name'],
433                             type=data[NS + 'type'],
434                             association=self.peek(),
435                             isNavigable=data[NS + 'isNavigable'])
436         self.peek().add_association_end(ae)
437         self.push(ae)
438
439     def end_AssociationEnd(self):
440         self.pop()
441
442     def start_Attribute(self, data):
443         assert isinstance(