root/gaphor/tags/gaphor-0.3.0/gaphor/UML/umllex.py

Revision 220, 14.7 kB (checked in by arjanmol, 5 years ago)

*** empty log message ***

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/env python
2 # vim:sw=4:et
3 """Lexical analizer for attributes and operations.
4
5 In this module some parse functions are added for attributes and operations.
6 The regular expressions are constructed based on a series of
7 "sub-patterns". This makes it easy to identify the autonomy of an
8 attribute/operation.
9 """
10
11 __all__ = [ 'parse_property', 'parse_operation', 'render_property', 'render_operation' ]
12
13 import re
14 from cStringIO import StringIO
15 import gaphor
16
17 # Visibility (optional) ::= '+' | '-' | '#'
18 vis_subpat = r'\s*(?P<vis>[-+#])?'
19
20 # Derived value (optional) ::= [/]
21 derived_subpat = r'\s*(?P<derived>/)?'
22
23 # name (required) ::= name
24 name_subpat = r'\s*(?P<name>\w+)'
25
26 # Multiplicity (added to type_subpat) ::= '[' [mult_l ..] mult_u ']'
27 mult_subpat = r'\s*(\[\s*((?P<mult_l>[0-9]+)\s*\.\.)?\s*(?P<mult_u>([0-9]+|\*))\s*\])?'
28 multa_subpat = r'\s*(((?P<mult_l>[0-9]+)\s*\.\.)?\s*(?P<mult_u>([0-9]+|\*)))?'
29
30 # Type and multiplicity (optional) ::= ':' type [mult]
31 type_subpat = r'\s*(:\s*(?P<type>\w+)\s*' + mult_subpat + r')?'
32
33 # default value (optional) ::= '=' default
34 default_subpat = r'\s*(=\s*(?P<default>\S+))?'
35
36 # tagged values (optional) ::= '{' tags '}'
37 tags_subpat = r'\s*(\{\s*(?P<tags>.*?)\s*\})?'
38
39 # Parameters (required) ::= '(' [params] ')'
40 params_subpat = r'\s*\(\s*(?P<params>[^)]+)?\)'
41
42 # Possible other parameters (optional) ::= ',' rest
43 rest_subpat = r'\s*(,\s*(?P<rest>.*))?'
44
45 # Direction of a parameter (optional, default in) ::= 'in' | 'out' | 'inout'
46 dir_subpat = r'\s*((?P<dir>in|out|inout)\s)?'
47
48 # Some trailing garbage => no valid syntax...
49 garbage_subpat = r'\s*(?P<garbage>.*)'
50
51 def compile(regex):
52     return re.compile(regex, re.MULTILINE | re.S)
53
54 # Attribute:
55 #   [+-#] [/] name [: type[\[mult\]]] [= default] [{ tagged values }]
56 attribute_pat = compile(r'^' + vis_subpat + derived_subpat + name_subpat + type_subpat + default_subpat + tags_subpat + garbage_subpat)
57
58 # Association end name:
59 #   [[+-#] [/] name [\[mult\]]] [{ tagged values }]
60 association_end_name_pat = compile(r'^' + '(' + vis_subpat + derived_subpat + name_subpat + mult_subpat + ')?' + tags_subpat + garbage_subpat)
61
62 # Association end multiplicity:
63 #   [mult] [{ tagged values }]
64 association_end_mult_pat = compile(r'^' + multa_subpat + tags_subpat + garbage_subpat)
65
66 # Operation:
67 #   [+|-|#] name ([parameters]) [: type[\[mult\]]] [{ tagged values }]
68 operation_pat = compile(r'^' + vis_subpat + name_subpat + params_subpat + type_subpat + tags_subpat + garbage_subpat)
69
70 # One parameter supplied with an operation:
71 #   [in|out|inout] name [: type[\[mult\]] [{ tagged values }]
72 parameter_pat = compile(r'^' + dir_subpat + name_subpat + type_subpat + default_subpat + tags_subpat + rest_subpat)
73
74
75 def _set_visibility(self, vis):
76     if vis == '+':
77         self.visibility = 'public'
78     elif vis == '#':
79         self.visibility = 'protected'
80     elif vis == '~':
81         self.visibility = 'package'
82     elif vis == '-':
83         self.visibility = 'private'
84     else:
85         try:
86             del self.visibility
87         except AttributeError:
88             pass
89
90 def parse_attribute(self, s):
91     """Parse string s in the property. Tagged values, multiplicity and stuff
92     like that is altered to reflect the data in the property string.
93     """
94     m = attribute_pat.match(s)
95     g = m.group
96     if not m or g('garbage'):
97         self.name = s
98         del self.visibility
99         del self.isDerived
100         if self.typeValue:
101             self.typeValue.value = None
102         if self.lowerValue:
103             self.lowerValue.value = None
104         if self.upperValue:
105             self.upperValue.value = None
106         if self.defaultValue:
107             self.defaultValue.value = None
108         if self.taggedValue:
109             self.taggedValue.value = None
110     else:
111         from uml2 import LiteralSpecification
112         create = self._factory.create
113         _set_visibility(self, g('vis'))
114         self.isDerived = g('derived') and True or False
115         self.name = g('name')
116         if not self.typeValue:
117             self.typeValue = create(LiteralSpecification)
118         self.typeValue.value = g('type')
119         if not self.lowerValue:
120             self.lowerValue = create(LiteralSpecification)
121         self.lowerValue.value = g('mult_l')
122         if not self.upperValue:
123             self.upperValue = create(LiteralSpecification)
124         self.upperValue.value = g('mult_u')
125         if not self.defaultValue:
126             self.defaultValue = create(LiteralSpecification)
127         self.defaultValue.value = g('default')
128         if not self.taggedValue:
129             self.taggedValue = create(LiteralSpecification)
130         self.taggedValue.value = g('tags')
131         #print g('vis'), g('derived'), g('name'), g('type'), g('mult_l'), g('mult_u'), g('default'), g('tags')
132
133 def parse_association_end(self, s):
134     """Parse the text at one end of an association. The association end holds
135     two strings. It is automattically figured out which string is fed to the
136     parser.
137     """
138     from uml2 import LiteralSpecification
139     create = self._factory.create
140     m = association_end_mult_pat.match(s)
141     if m and m.group('mult_u') or m.group('tags'):
142         #print 'multmatch'
143         g = m.group
144         if not self.lowerValue:
145             self.lowerValue = create(LiteralSpecification)
146         self.lowerValue.value = g('mult_l')
147         if not self.upperValue:
148             self.upperValue = create(LiteralSpecification)
149         self.upperValue.value = g('mult_u')
150         if not self.taggedValue:
151             self.taggedValue = create(LiteralSpecification)
152         self.taggedValue.value = g('tags')
153         # We have multiplicity
154     else:
155         #print 'namematch'
156         m = association_end_name_pat.match(s)
157         g = m.group
158         if g('garbage'):
159             self.name = s
160             del self.visibility
161             del self.isDerived
162         else:
163             _set_visibility(self, g('vis'))
164             self.isDerived = g('derived') and True or False
165             self.name = g('name')
166             # Optionally, the multiplicity and tagged values may be defined:
167             if g('mult_l'):
168                 if not self.lowerValue:
169                     self.lowerValue = create(LiteralSpecification)
170                 self.lowerValue.value = g('mult_l')
171             if g('mult_u'):
172                 if not g('mult_l') and self.lowerValue:
173                     self.lowerValue.value = None
174                 if not self.upperValue:
175                     self.upperValue = create(LiteralSpecification)
176                 self.upperValue.value = g('mult_u')
177             if g('tags'):
178                 if not self.taggedValue:
179                     self.taggedValue = create(LiteralSpecification)
180                 self.taggedValue.value = g('tags')
181
182 def parse_property(self, s):
183     if self.association:
184         parse_association_end(self, s)
185     else:
186         parse_attribute(self, s)
187
188 def parse_operation(self, s):
189     """Parse string s in the operation. Tagged values, parameters and
190     visibility is altered to reflect the data in the operation string.
191     """
192     from uml2 import Parameter, LiteralSpecification
193     m = operation_pat.match(s)
194     if not m or m.group('garbage'):
195         self.name = s
196         del self.visibility
197         map(Parameter.unlink, list(self.returnResult))
198         map(Parameter.unlink, list(self.formalParameter))
199     else:
200         g = m.group
201         create = self._factory.create
202         _set_visibility(self, g('vis'))
203         self.name = g('name')
204         if not self.returnResult:
205             self.returnResult = create(Parameter)
206         p = self.returnResult[0]
207         p.direction = 'return'
208         if not p.typeValue:
209             p.typeValue = create(LiteralSpecification)
210         p.typeValue.value = g('type')
211         if not p.lowerValue:
212             p.lowerValue = create(LiteralSpecification)
213         p.lowerValue.value = g('mult_l')
214         if not p.upperValue:
215             p.upperValue = create(LiteralSpecification)
216         p.upperValue.value = g('mult_u')
217         # FIXME: Maybe add to Operation.ownedRule?
218         if not p.taggedValue:
219             p.taggedValue = create(LiteralSpecification)
220         p.taggedValue.value = g('tags')
221         #print g('vis'), g('name'), g('type'), g('mult_l'), g('mult_u'), g('tags')
222        
223         pindex = 0
224         params = g('params')
225         while params:
226             m = parameter_pat.match(params)
227             if not m:
228                 break
229             g = m.group
230             try:
231                 p = self.formalParameter[pindex]
232             except IndexError:
233                 p = create(Parameter)
234             p.direction = g('dir') or 'in'
235             p.name = g('name')
236             if not p.typeValue:
237                 p.typeValue = create(LiteralSpecification)
238             p.typeValue.value = g('type')
239             if not p.lowerValue:
240                 p.lowerValue = create(LiteralSpecification)
241             p.lowerValue.value = g('mult_l')
242             if not p.upperValue:
243                 p.upperValue = create(LiteralSpecification)
244             p.upperValue.value = g('mult_u')
245             if not p.defaultValue:
246                 p.defaultValue = create(LiteralSpecification)
247             p.defaultValue.value = g('default')
248             if not p.taggedValue:
249                 p.taggedValue = create(LiteralSpecification)
250             p.taggedValue.value = g('tags')
251             self.formalParameter = p
252
253             #print ' ', g('dir') or 'in', g('name'), g('type'), g('mult_l'), g('mult_u'), g('default'), g('tags')
254
255             # Do the next parameter:
256             params = g('rest')
257             pindex += 1
258
259         # Remove remaining parameters:
260         for fp in self.formalParameter[pindex:]:
261             fp.unlink()
262
263 # Do not render if the name still contains a visibility element
264 no_render_pat = compile(r'^\s*[+#-]')
265 vis_map = {
266     'public': '+',
267     'protected': '#',
268     'package': '~',
269     'private': '-'
270 }
271
272 def render_attribute(self, visibility=False, is_derived=False, type=False,
273                            multiplicity=False, default=False, tags=False):
274     """Create a OCL representation of the attribute,
275     Returns the attribute as a string.
276     If one or more of the parameters (visibility, is_derived, type,
277     multiplicity, default and/or tags) is set, only that field is rendered.
278     Note that the name of the attribute is always rendered, so a parseable
279     string is returned.
280
281     Note that, when some of those parameters are set, parsing the string
282     will not give you the same result.
283     """
284     name = self.name
285     if not name or no_render_pat.match(name):
286         return name
287
288     # Render all fields if they all are set to False
289     if not (visibility or is_derived or type or multiplicity or default or tags):
290        visibility = is_derived = type = multiplicity = default = tags = True
291
292     s = StringIO()
293
294     if visibility:
295         s.write(vis_map[self.visibility])
296         s.write(' ')
297
298     if is_derived:
299         if self.isDerived: s.write('/')
300
301     s.write(name)
302    
303     if type and self.typeValue and self.typeValue.value:
304         s.write(': %s' % self.typeValue.value)
305
306     if multiplicity and self.upperValue and self.upperValue.value: 
307         if self.lowerValue and self.lowerValue.value:
308             s.write('[%s..%s]' % (self.lowerValue.value, self.upperValue.value))
309         else:
310             s.write('[%s]' % self.upperValue.value)
311
312     if default and self.defaultValue and self.defaultValue.value:
313         s.write(' = %s' % self.defaultValue.value)
314
315     if tags and self.taggedValue and self.taggedValue.value:
316         s.write(' { %s }' % self.taggedValue.value)
317     s.reset()
318     return s.read()
319
320 def render_association_end(self):
321     """
322     """
323     name = ''
324     n = StringIO()
325     if self.name:
326         n.write(vis_map[self.visibility])
327         n.write(' ')
328         if self.isDerived:
329             n.write('/')
330         if self.name:
331             n.write(self.name)
332         n.reset()
333         name = n.read()
334
335     m = StringIO()
336     if self.upperValue and self.upperValue.value: 
337         if self.lowerValue and self.lowerValue.value:
338             #print 'render_association_end:', self.lowerValue.value, self.upperValue.value
339             m.write('%s..%s' % (self.lowerValue.value, self.upperValue.value))
340         else:
341             m.write('%s' % self.upperValue.value)
342     if self.taggedValue and self.taggedValue.value:
343         m.write(' {%s}' % self.taggedValue.value)
344     m.reset()
345     mult = m.read()
346
347     #print 'render_association_end', name, mult
348     return name, mult
349
350 def render_property(self, *args, **kwargs):
351     """Render a gaphor.UML.Property either as an attribute or as a
352     (name, multiplicity) tuple for association ends.
353
354     See also: render_attribute, render_association_end
355     """
356     if self.association:
357         return render_association_end(self)
358     else:
359         return render_attribute(self, *args, **kwargs)
360
361 def render_operation(self, visibility=False, type=False, multiplicity=False,
362                            default=False, tags=False, direction=False):
363     """Create a OCL representation of the operation,
364     Returns the operation as a string.
365     """
366     name = self.name
367     if no_render_pat.match(name):
368         return name
369
370     # Render all fields if they all are set to False
371     if not (visibility or type or multiplicity or default or tags or direction):
372        visibility = type = multiplicity = default = tags = direction = True
373
374     s = StringIO()
375     if visibility:
376         s.write(vis_map[self.visibility])
377         s.write(' ')
378
379     s.write(name)
380     s.write('(')
381
382     for p in self.formalParameter:
383         #print p # direction, name, type, mult, default, tags
384         if direction:
385             s.write(p.direction)
386             s.write(' ')
387         s.write(p.name)
388         if type and p.typeValue and p.typeValue.value:
389             s.write(': %s' % p.typeValue.value)
390         if multiplicity and p.upperValue and p.upperValue.value: 
391             if p.lowerValue and p.lowerValue.value:
392                 s.write('[%s..%s]' % (p.lowerValue.value, p.upperValue.value))
393             else:
394                 s.write('[%s]' % p.upperValue.value)
395         if default and p.defaultValue and p.defaultValue.value:
396             s.write(' = %s' % p.defaultValue.value)
397         if tags and  p.taggedValue and p.taggedValue.value:
398             s.write(' { %s }' % p.taggedValue.value)
399         if p is not self.formalParameter[-1]:
400             s.write(', ')
401
402     s.write(')')
403
404     rr = self.returnResult and self.returnResult[0]
405     if rr:
406         if type and rr.typeValue and rr.typeValue.value:
407             s.write(': %s' % rr.typeValue.value)
408         if multiplicity and rr.upperValue and rr.upperValue.value: 
409             if rr.lowerValue and rr.lowerValue.value:
410                 s.write('[%s..%s]' % (rr.lowerValue.value, rr.upperValue.value))
411             else:
412                 s.write('[%s]' % rr.upperValue.value)
413         if tags and rr.taggedValue and rr.taggedValue.value:
414             s.write(' { %s }' % rr.taggedValue.value)
415     s.reset()
416     return s.read()
Note: See TracBrowser for help on using the browser.