umlmeta 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
umlmeta/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """umlmeta — clean-room UML 2.5.1 / SysML v1 metamodel and the SysML v1 -> v2
2
+ transformation, generated from OMG-published documents.
3
+
4
+ The generated metamodel modules (:mod:`umlmeta.uml25`, :mod:`umlmeta.sysml`,
5
+ :mod:`umlmeta.sysml2`, :mod:`umlmeta.uaf`, :mod:`umlmeta.uaf13`,
6
+ :mod:`umlmeta.standard_profile`, :mod:`umlmeta.bpmn`) are pure stdlib and
7
+ self-contained. The library modules around them (XMI/CMOF/EMOF readers and
8
+ writers, the textual and abstract-syntax v1->v2 transformers, normative
9
+ derivations, query helpers) are importable as ``umlmeta.<module>``.
10
+
11
+ Clean-room discipline: every semantic is traceable to an OMG document.
12
+ """
13
+
14
+ __version__ = "0.2.0"
15
+
16
+ __all__ = [
17
+ "bpmn",
18
+ "boxes_view",
19
+ "canon",
20
+ "derived",
21
+ "mdzip",
22
+ "mdzip_diff",
23
+ "mdzip_import",
24
+ "mm_write",
25
+ "ocl_translator",
26
+ "query",
27
+ "standard_profile",
28
+ "sysml",
29
+ "sysml2",
30
+ "uaf",
31
+ "uaf13",
32
+ "uml25",
33
+ "v1_to_v2",
34
+ "v1_to_v2_as",
35
+ "xmi21",
36
+ "xmi_write",
37
+ ]
umlmeta/boxes_view.py ADDED
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env python3
2
+ """umlmeta boxes_view — render v1 data (UML 2.5.1 / SysML v1 model
3
+ objects) as SysML v1 diagrams through the sibling diagramboxes package,
4
+ the same pattern as sysmlpy's boxes_view adapters.
5
+
6
+ Supported views (this commit): Block Definition Diagram (bdd).
7
+ - A block = a Class (or DataType) carrying the Block stereotype (or any
8
+ Class under the walked scope when the model is plain UML).
9
+ - Part properties (aggregation == 'composite') / value properties
10
+ (ValueProperty stereotype) / plain properties render as rows in the
11
+ block's parts / values compartments — Delligatti §3.4/Fig 3.5:
12
+ properties are COMPARTMENT STRINGS, not nested boxes.
13
+ - Ports render on the block's boundary (§4.12); flow specs on a port
14
+ surface the port's items as its label.
15
+ - Generalizations render as open-triangle edges to the general class.
16
+ - Reference properties (ReferenceProperty stereotype) render as rows in
17
+ a 'references' compartment (§3.4).
18
+ - Packages render as folder-tab views (§10.4) holding their owned
19
+ packages and blocks.
20
+
21
+ Rendering = diagramboxes (diagramboxes on the path); the caller renders
22
+ braille (`Diagram.render()`) or SVG (`render_svg()`).
23
+ """
24
+ import os
25
+ import sys
26
+
27
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__))))
28
+
29
+ try:
30
+ from diagramboxes import Diagram
31
+ except ImportError:
32
+ if os.path.isdir(os.path.expanduser('~/proj/diagramboxes')):
33
+ sys.path.insert(0, os.path.expanduser('~/proj/diagramboxes'))
34
+ from diagramboxes import Diagram
35
+ else:
36
+ raise
37
+
38
+ def _stereo_name(obj):
39
+ """The stereotype name for an element's `_applied_stereotypes`
40
+ list of (profile, stereo, app_id) tuples."""
41
+ apps = getattr(obj, '_applied_stereotypes', None) or []
42
+ if not apps:
43
+ return None
44
+ return apps[0][1]
45
+
46
+
47
+ def _block_sides(n):
48
+ """Alternate left/right for boundary ports (the BDD port spread)."""
49
+ return 'left' if n % 2 == 0 else 'right'
50
+
51
+
52
+ def as_bdd_boxes(scope, frame_kind='bdd', element_kind=None,
53
+ element_name=None, diagram_name=None):
54
+ """Build a Block Definition Diagram from umlmeta data.
55
+
56
+ Parameters
57
+ ----------
58
+ scope : a Package / Model / Namespace object (umlmeta generated
59
+ metaclasses), or a list of elements.
60
+ frame_* : the diagram-kind frame strings (Delligatti §2.4).
61
+
62
+ Returns
63
+ -------
64
+ diagramboxes.Diagram
65
+ """
66
+ d = Diagram()
67
+ if frame_kind:
68
+ d.add_frame(frame_kind, element_kind, element_name,
69
+ diagram_name)
70
+
71
+ pkg_views = {} # package id -> View (folder tab)
72
+ block_nodes = {} # class id -> Node
73
+ ports_seen = {} # class id -> count (for side spreading)
74
+
75
+ def _walk_pkg(pkg, parent_view=None):
76
+ el = pkg
77
+
78
+ view = None
79
+ pname = getattr(el, 'name', None)
80
+ if pname is not None and type(el).__name__ in (
81
+ 'Package', 'Model', 'Profile'):
82
+ view = parent_view or d.add_view(
83
+ pname, stereotypes=['package'])
84
+ pkg_views[id(el)] = view
85
+
86
+ blocks = []
87
+ for child in (getattr(el, 'packagedElement', None) or []):
88
+ if type(child).__name__ == 'Package':
89
+ _walk_pkg(child, view)
90
+ continue
91
+ if type(child).__name__ in ('Class', 'DataType', 'Signal',
92
+ 'Enumeration', 'Interface'):
93
+ blocks.append(child)
94
+
95
+ # two passes: every block draws BEFORE any generalization edge
96
+ # resolves its target (a generalization to a later sibling must
97
+ # not mint a duplicate node)
98
+ for b in blocks:
99
+ _draw_block(b, view, draw_edges=False)
100
+ for b in blocks:
101
+ _block_edges(b)
102
+
103
+ def _draw_block(b, view, draw_edges=True):
104
+ shape = _block_shape(b, view)
105
+ if draw_edges:
106
+ _block_edges(b)
107
+ return shape
108
+
109
+ def _block_shape(b, view):
110
+ kind = type(b).__name__
111
+ stereo = _stereo_name(b)
112
+ # the frame's block context suppresses a bare «block»; anything
113
+ # else (Signal, Interface, «value type», «enumeration»…) prints
114
+ st_lines = None if stereo in (None, 'Block') else [stereo]
115
+ node = d.add_node(getattr(b, 'name', None) or 'unnamed',
116
+ stereotypes=st_lines)
117
+ if view is not None:
118
+ view.add_child(node)
119
+ block_nodes[id(b)] = node
120
+
121
+ # ports on the boundary (flow-qualified labels, §4.12)
122
+ n = 0
123
+ for p in list(getattr(b, 'ownedAttribute', None) or []):
124
+ if type(p).__name__ != 'Port':
125
+ continue
126
+ lab = getattr(p, 'name', None) or ''
127
+ t = getattr(p, 'type', None)
128
+ if t is not None and getattr(t, 'name', None):
129
+ lab = '%s : %s' % (lab, t.name)
130
+ node.add_port(lab, side=_block_sides(n), direction=None)
131
+ n += 1
132
+ ports_seen[id(b)] = n
133
+
134
+ # compartment rows: parts / values / references / plain
135
+ parts, values, refs, plain = [], [], [], []
136
+ for p in (getattr(b, 'ownedAttribute', None) or []):
137
+ if type(p).__name__ == 'Port':
138
+ continue
139
+ tname = getattr(getattr(p, 'type', None), 'name', None)
140
+ mult = _end_multiplicity(b, p)
141
+ nm = getattr(p, 'name', None)
142
+ if not nm:
143
+ continue
144
+ stereo = _stereo_name(p)
145
+ s = (p.aggregation if isinstance(p.aggregation, str) else None) \
146
+ if hasattr(p, 'aggregation') else None
147
+ is_part = (s == 'composite')
148
+ is_ref = stereo in ('ReferenceProperty',) or s == 'shared'
149
+ if stereo == 'ValueProperty' or stereo == 'Measurement':
150
+ values.append((nm, tname, mult))
151
+ elif is_ref:
152
+ refs.append((nm, tname, mult))
153
+ elif is_part:
154
+ parts.append((nm, tname, mult))
155
+ else:
156
+ plain.append((nm, tname, mult))
157
+
158
+ def _row(nm, tname, mult):
159
+ s = '%s : %s' % (nm, tname) if tname else nm
160
+ if mult:
161
+ s += ' [%s]' % mult
162
+ return s
163
+
164
+ if values:
165
+ node.compartments = list(getattr(node, 'compartments', None) or [])
166
+ node.compartments.append(
167
+ ('values', [_row(*r) for r in values]))
168
+ if parts:
169
+ node.compartments = list(getattr(node, 'compartments', None) or [])
170
+ node.compartments.append(
171
+ ('parts', [_row(*r) for r in parts]))
172
+ if refs:
173
+ node.compartments = list(getattr(node, 'compartments', None) or [])
174
+ node.compartments.append(
175
+ ('references', [_row(*r) for r in refs]))
176
+ if plain:
177
+ node.attributes = [_row(*r) for r in plain]
178
+
179
+ def _block_edges(b):
180
+ # generalizations: open triangle at the general class
181
+ for g in (getattr(b, 'generalization', None) or []):
182
+ gcls = getattr(g, 'general', None)
183
+ if gcls is None:
184
+ continue
185
+ node = block_nodes.get(id(b))
186
+ target_node = block_nodes.get(id(gcls))
187
+ if node is None or target_node is None:
188
+ continue # target outside the walked scope
189
+ d.add_edge(node, target_node, target_style='triangle')
190
+ node._calc_size(); target_node._calc_size()
191
+
192
+ def _end_multiplicity(owner, prop):
193
+ # the multiplicity of the end that OWNS this property is the
194
+ # property's own lower/upper
195
+ lo = getattr(prop, 'lower', None)
196
+ up = getattr(prop, 'upper', None)
197
+ if lo is None and up is None:
198
+ return None
199
+ if (lo or '0') == '1' and (up or '1') == '1':
200
+ return None # 1..1 = default, omitted (Delligatti §3.4)
201
+ if (up or lo) == (lo or up):
202
+ return lo or up
203
+ return '%s..%s' % (lo, up)
204
+
205
+ if isinstance(scope, (list, tuple)):
206
+ for el in scope:
207
+ if type(el).__name__ in ('Package', 'Model'):
208
+ _walk_pkg(el)
209
+ else:
210
+ _draw_block(el)
211
+ else:
212
+ _walk_pkg(scope)
213
+
214
+ return d
215
+
216
+
217
+ def as_deployment_boxes(scope, frame_kind='deployment',
218
+ element_kind=None, element_name=None):
219
+ """Build a UML 2.5.1 deployment diagram (ch19) from umlmeta data.
220
+
221
+ - Node, Device, ExecutionEnvironment, or an InstanceSpecification
222
+ of one = a perspective cube (instance labels are the ':name'
223
+ string); the deployment's elements connect in.
224
+ - Artifact = a Class rectangle + the keyword artifact.
225
+ - Deployment.deployedArtifact x location/deployment's owner = the
226
+ dashed 'deploy' dependency arrow (19.2.4).
227
+ - Artifact.manifestation[] = dashed 'manifest' lines (19.3.x).
228
+ - CommunicationPath memberEnds = plain association links (19.4.4).
229
+ """
230
+ d = Diagram()
231
+ if frame_kind:
232
+ d.add_frame(frame_kind, element_kind, element_name, None)
233
+
234
+ def _name(el, default='unnamed'):
235
+ return getattr(el, 'name', None) or default
236
+
237
+ def _node_of(el, inst=False):
238
+ kinds = []
239
+ st = getattr(el, '_applied_stereotypes', None)
240
+ if st:
241
+ kinds = [s[1] for s in st]
242
+ return d.node(_name(el), kinds=kinds)
243
+
244
+ node_map = {}
245
+
246
+ def _add(el, cube):
247
+ cube_ = d.node(_name(el), kinds=[]) if cube else d.add_node(
248
+ _name(el), stereotypes=['artifact'])
249
+ node_map[id(el)] = cube_
250
+ d.nodes.append(cube_)
251
+ return cube_
252
+
253
+ def _walk(pkg):
254
+ for child in (getattr(pkg, 'packagedElement', None) or []):
255
+ tn = type(child).__name__
256
+ if tn == 'Package':
257
+ _walk(child)
258
+ elif tn == 'Node':
259
+ node_map[id(child)] = _node_of(child)
260
+ elif tn == 'Artifact':
261
+ node_map[id(child)] = d.artifact(_name(child))
262
+ elif tn == 'InstanceSpecification':
263
+ node_map[id(child)] = d.node(_name(child))
264
+ elif tn == 'Component':
265
+ node_map[id(child)] = d.add_node(_name(child))
266
+ # CommunicationPath (19.4.4): a plain association link between
267
+ # its endpoint types
268
+ for child in (getattr(pkg, 'packagedElement', None) or []):
269
+ if type(child).__name__ != 'CommunicationPath':
270
+ continue
271
+ ends = [p for p in (getattr(child, 'memberEnd', None) or [])
272
+ if getattr(p, 'type', None) is not None]
273
+ if len(ends) == 2:
274
+ d.add_edge(node_map.get(id(ends[0].type)),
275
+ node_map.get(id(ends[1].type)))
276
+ # deployments owned by each element
277
+ for child in (getattr(pkg, 'packagedElement', None) or []):
278
+ for dep in (getattr(child, 'deployment', None) or []):
279
+ for art in (getattr(dep, 'deployedArtifact', None) or []):
280
+ if isinstance(art, (str, int)):
281
+ continue
282
+ if id(art) not in node_map:
283
+ node_map[id(art)] = d.artifact(_name(art))
284
+ d.deploy(node_map[id(art)], node_map[id(child)])
285
+ for man in (getattr(child, 'manifestation', None) or []):
286
+ util = getattr(man, 'utilizedElement', None)
287
+ if util is None:
288
+ continue
289
+ if id(util) not in node_map:
290
+ node_map[id(util)] = d.add_node(_name(util))
291
+ d.manifest(node_map[id(child)], node_map[id(util)])
292
+
293
+ if isinstance(scope, (list, tuple)):
294
+ for el in scope:
295
+ _walk(el)
296
+ else:
297
+ _walk(scope)
298
+
299
+ d.render()
300
+ return d
301
+
302
+
303
+ def render_bdd_boxes(scope, routing='orthogonal', **layout_kw) -> str:
304
+ d = as_bdd_boxes(scope)
305
+ d.render()
306
+ return d.render()
307
+
308
+
309
+ def render_bdd_boxes_svg(scope, routing='orthogonal', **layout_kw) -> str:
310
+ d = as_bdd_boxes(scope)
311
+ return d.render_svg(routing=routing)