sysmlpy 0.1.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.
- sysmlpy/__init__.py +226 -0
- sysmlpy/__main__.py +75 -0
- sysmlpy/antlr/README.md +146 -0
- sysmlpy/antlr/SysMLv2Lexer.interp +695 -0
- sysmlpy/antlr/SysMLv2Lexer.py +1123 -0
- sysmlpy/antlr/SysMLv2Lexer.tokens +444 -0
- sysmlpy/antlr/SysMLv2Parser.interp +960 -0
- sysmlpy/antlr/SysMLv2Parser.py +40750 -0
- sysmlpy/antlr/SysMLv2Parser.tokens +444 -0
- sysmlpy/antlr/SysMLv2ParserListener.py +4485 -0
- sysmlpy/antlr/SysMLv2ParserVisitor.py +2498 -0
- sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +695 -0
- sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +1123 -0
- sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +444 -0
- sysmlpy/antlr_parser.py +185 -0
- sysmlpy/antlr_visitor.py +10864 -0
- sysmlpy/definition.py +1115 -0
- sysmlpy/examples/attribute_values.py +161 -0
- sysmlpy/examples/part_attributes.py +139 -0
- sysmlpy/examples/tuples_sequences.py +231 -0
- sysmlpy/formatting.py +47 -0
- sysmlpy/grammar/antlr4/README.md +30 -0
- sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +931 -0
- sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +695 -0
- sysmlpy/grammar/antlr4/SysMLv2Lexer.py +1123 -0
- sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +444 -0
- sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +2432 -0
- sysmlpy/grammar/antlr4/SysMLv2Parser.interp +960 -0
- sysmlpy/grammar/antlr4/SysMLv2Parser.py +40750 -0
- sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +444 -0
- sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +4485 -0
- sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +2498 -0
- sysmlpy/grammar/antlr4/desc.xml +7 -0
- sysmlpy/grammar/antlr4/examples/camera.sysml +14 -0
- sysmlpy/grammar/antlr4/examples/toaster-system.sysml +193 -0
- sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +118 -0
- sysmlpy/grammar/antlr4/pom.xml +59 -0
- sysmlpy/grammar/classes.py +8610 -0
- sysmlpy/library/LICENSE +165 -0
- sysmlpy/library/README.md +52 -0
- sysmlpy/library/__init__.py +0 -0
- sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +34 -0
- sysmlpy/library/domain/Analysis/SampledFunctions.sysml +119 -0
- sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +143 -0
- sysmlpy/library/domain/Analysis/TradeStudies.sysml +171 -0
- sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +83 -0
- sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +81 -0
- sysmlpy/library/domain/Geometry/ShapeItems.sysml +899 -0
- sysmlpy/library/domain/Geometry/SpatialItems.sysml +168 -0
- sysmlpy/library/domain/Metadata/ImageMetadata.sysml +78 -0
- sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +143 -0
- sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +39 -0
- sysmlpy/library/domain/Metadata/RiskMetadata.sysml +100 -0
- sysmlpy/library/domain/Quantities and Units/ISQ.sysml +42 -0
- sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +439 -0
- sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +2726 -0
- sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +206 -0
- sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +1991 -0
- sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +1353 -0
- sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +1223 -0
- sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +2333 -0
- sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +958 -0
- sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +1537 -0
- sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +1564 -0
- sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +1169 -0
- sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +1256 -0
- sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +30 -0
- sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +526 -0
- sysmlpy/library/domain/Quantities and Units/Quantities.sysml +107 -0
- sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +70 -0
- sysmlpy/library/domain/Quantities and Units/SI.sysml +378 -0
- sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +48 -0
- sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +50 -0
- sysmlpy/library/domain/Quantities and Units/Time.sysml +274 -0
- sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +260 -0
- sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +62 -0
- sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +63 -0
- sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +39 -0
- sysmlpy/library/kernel/Base.kerml +95 -0
- sysmlpy/library/kernel/BaseFunctions.kerml +80 -0
- sysmlpy/library/kernel/BooleanFunctions.kerml +22 -0
- sysmlpy/library/kernel/Clocks.kerml +156 -0
- sysmlpy/library/kernel/CollectionFunctions.kerml +68 -0
- sysmlpy/library/kernel/Collections.kerml +147 -0
- sysmlpy/library/kernel/ComplexFunctions.kerml +47 -0
- sysmlpy/library/kernel/ControlFunctions.kerml +117 -0
- sysmlpy/library/kernel/ControlPerformances.kerml +135 -0
- sysmlpy/library/kernel/DataFunctions.kerml +43 -0
- sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +190 -0
- sysmlpy/library/kernel/IntegerFunctions.kerml +43 -0
- sysmlpy/library/kernel/KerML.kerml +483 -0
- sysmlpy/library/kernel/Links.kerml +67 -0
- sysmlpy/library/kernel/Metaobjects.kerml +58 -0
- sysmlpy/library/kernel/NaturalFunctions.kerml +27 -0
- sysmlpy/library/kernel/NumericalFunctions.kerml +43 -0
- sysmlpy/library/kernel/Objects.kerml +212 -0
- sysmlpy/library/kernel/Observation.kerml +161 -0
- sysmlpy/library/kernel/OccurrenceFunctions.kerml +154 -0
- sysmlpy/library/kernel/Occurrences.kerml +992 -0
- sysmlpy/library/kernel/Performances.kerml +293 -0
- sysmlpy/library/kernel/RationalFunctions.kerml +49 -0
- sysmlpy/library/kernel/RealFunctions.kerml +56 -0
- sysmlpy/library/kernel/ScalarFunctions.kerml +33 -0
- sysmlpy/library/kernel/ScalarValues.kerml +23 -0
- sysmlpy/library/kernel/SequenceFunctions.kerml +111 -0
- sysmlpy/library/kernel/SpatialFrames.kerml +197 -0
- sysmlpy/library/kernel/StatePerformances.kerml +145 -0
- sysmlpy/library/kernel/StringFunctions.kerml +25 -0
- sysmlpy/library/kernel/Transfers.kerml +281 -0
- sysmlpy/library/kernel/TransitionPerformances.kerml +66 -0
- sysmlpy/library/kernel/TrigFunctions.kerml +35 -0
- sysmlpy/library/kernel/Triggers.kerml +188 -0
- sysmlpy/library/kernel/VectorFunctions.kerml +273 -0
- sysmlpy/library/kernel/VectorValues.kerml +64 -0
- sysmlpy/library/systems/Actions.sysml +552 -0
- sysmlpy/library/systems/Allocations.sysml +29 -0
- sysmlpy/library/systems/AnalysisCases.sysml +38 -0
- sysmlpy/library/systems/Attributes.sysml +25 -0
- sysmlpy/library/systems/Calculations.sysml +37 -0
- sysmlpy/library/systems/Cases.sysml +71 -0
- sysmlpy/library/systems/Connections.sysml +60 -0
- sysmlpy/library/systems/Constraints.sysml +44 -0
- sysmlpy/library/systems/Flows.sysml +126 -0
- sysmlpy/library/systems/Interfaces.sysml +89 -0
- sysmlpy/library/systems/Items.sysml +153 -0
- sysmlpy/library/systems/Metadata.sysml +32 -0
- sysmlpy/library/systems/Parts.sysml +81 -0
- sysmlpy/library/systems/Ports.sysml +54 -0
- sysmlpy/library/systems/Requirements.sysml +194 -0
- sysmlpy/library/systems/StandardViewDefinitions.sysml +123 -0
- sysmlpy/library/systems/States.sysml +103 -0
- sysmlpy/library/systems/SysML.sysml +539 -0
- sysmlpy/library/systems/UseCases.sysml +57 -0
- sysmlpy/library/systems/VerificationCases.sysml +103 -0
- sysmlpy/library/systems/Views.sysml +164 -0
- sysmlpy/navigate.py +272 -0
- sysmlpy/store.py +547 -0
- sysmlpy/usage.py +2978 -0
- sysmlpy-0.1.0.dist-info/METADATA +230 -0
- sysmlpy-0.1.0.dist-info/RECORD +143 -0
- sysmlpy-0.1.0.dist-info/WHEEL +4 -0
- sysmlpy-0.1.0.dist-info/entry_points.txt +3 -0
- sysmlpy-0.1.0.dist-info/licenses/LICENSE +21 -0
sysmlpy/definition.py
ADDED
|
@@ -0,0 +1,1115 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Tue Jul 11 10:14:18 2023
|
|
5
|
+
|
|
6
|
+
@author: christophercox
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import uuid as uuidlib
|
|
10
|
+
|
|
11
|
+
from typing import TypeVar
|
|
12
|
+
|
|
13
|
+
from sysmlpy.formatting import classtree
|
|
14
|
+
from sysmlpy.navigate import Searchable
|
|
15
|
+
from sysmlpy.grammar.classes import (
|
|
16
|
+
Identification,
|
|
17
|
+
PackageMember,
|
|
18
|
+
PackageBody,
|
|
19
|
+
RootNamespace,
|
|
20
|
+
)
|
|
21
|
+
from sysmlpy.grammar.classes import Package as PackageGrammar
|
|
22
|
+
|
|
23
|
+
from sysmlpy import Part, Item, Port, Requirement, UseCase, Attribute, Action, Case, AnalysisCase, VerificationCase
|
|
24
|
+
from sysmlpy.usage import (
|
|
25
|
+
State, Constraint, Connection, Flow, Calculation, Enumeration,
|
|
26
|
+
Allocation, Metadata, Rendering, Individual, FlowDef,
|
|
27
|
+
View, Viewpoint, Concern,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
ModelType = TypeVar("Model", bound="Model")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Model(Searchable):
|
|
34
|
+
"""Root model container. Exposes typed accessors and ``find()`` / ``all()``
|
|
35
|
+
across all child packages and their contents."""
|
|
36
|
+
|
|
37
|
+
sysml_type = None # Model is the root, not a SysML element itself
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
self.name = str(uuidlib.uuid4())
|
|
41
|
+
self.children = []
|
|
42
|
+
self.typedby = None
|
|
43
|
+
self.grammar = None
|
|
44
|
+
self.parent = None
|
|
45
|
+
|
|
46
|
+
def __repr__(self):
|
|
47
|
+
cls_name = self.__class__.__name__
|
|
48
|
+
if self.children:
|
|
49
|
+
children_repr = ', '.join(repr(c) for c in self.children)
|
|
50
|
+
return f"{cls_name}(children=[{children_repr}])"
|
|
51
|
+
return f"{cls_name}()"
|
|
52
|
+
|
|
53
|
+
def load(self: type[ModelType], s: str, library=None) -> ModelType:
|
|
54
|
+
"""Load a SysML model from a string using ANTLR4 parser.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
s : str
|
|
59
|
+
The SysML source code to parse.
|
|
60
|
+
library : str or Path, optional
|
|
61
|
+
Path to SysML v2 library files for resolving imports.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
Model
|
|
66
|
+
The loaded model.
|
|
67
|
+
"""
|
|
68
|
+
from sysmlpy import load_grammar_antlr
|
|
69
|
+
|
|
70
|
+
# Model requires the input to be a package declaration
|
|
71
|
+
# Check if the string starts with a valid SysML keyword that's not 'package' or 'library'
|
|
72
|
+
s_stripped = s.strip() if isinstance(s, str) else s
|
|
73
|
+
if isinstance(s_stripped, str):
|
|
74
|
+
# List of known SysML top-level keywords (definitions and usages that must be wrapped)
|
|
75
|
+
invalid_starts = ['item', 'part', 'port', 'attribute', 'action', 'state',
|
|
76
|
+
'connection', 'interface', 'requirement', 'case', 'use',
|
|
77
|
+
'calc', 'constraint', 'concern', 'ref', 'flow', 'allocation',
|
|
78
|
+
'view', 'viewpoint', 'rendering', 'metadata', 'individual']
|
|
79
|
+
first_word = s_stripped.split(None, 1)[0] if s_stripped.split() else ''
|
|
80
|
+
if first_word in invalid_starts:
|
|
81
|
+
raise ValueError("Base Model must be encapsulated by a package.")
|
|
82
|
+
|
|
83
|
+
definition = load_grammar_antlr(s, library=library)["ownedRelationship"]
|
|
84
|
+
|
|
85
|
+
member_grammar = []
|
|
86
|
+
found_package = False
|
|
87
|
+
for member in definition:
|
|
88
|
+
if "ownedRelatedElement" not in member:
|
|
89
|
+
if member.get("name") in ("Import", "AliasMember"):
|
|
90
|
+
member_grammar.append(member)
|
|
91
|
+
continue
|
|
92
|
+
if member["ownedRelatedElement"]["name"] == "DefinitionElement":
|
|
93
|
+
de = member["ownedRelatedElement"]
|
|
94
|
+
if de["ownedRelatedElement"]["name"] == "Package":
|
|
95
|
+
found_package = True
|
|
96
|
+
p = Package().load_from_grammar(
|
|
97
|
+
PackageGrammar(de["ownedRelatedElement"])
|
|
98
|
+
)
|
|
99
|
+
self.children.append(p)
|
|
100
|
+
member_grammar.append(p._get_definition(child="PackageBody"))
|
|
101
|
+
|
|
102
|
+
if not found_package:
|
|
103
|
+
# Wrap bare top-level definitions in a synthetic package
|
|
104
|
+
synthetic_relationships = []
|
|
105
|
+
for member in definition:
|
|
106
|
+
if "ownedRelatedElement" not in member:
|
|
107
|
+
if member.get("name") in ("Import", "AliasMember"):
|
|
108
|
+
member_grammar.append(member)
|
|
109
|
+
continue
|
|
110
|
+
if member["ownedRelatedElement"]["name"] == "DefinitionElement":
|
|
111
|
+
de = member["ownedRelatedElement"]
|
|
112
|
+
if de["ownedRelatedElement"]["name"] == "AnnotatingElement":
|
|
113
|
+
continue
|
|
114
|
+
synthetic_relationships.append({
|
|
115
|
+
"name": "PackageMember",
|
|
116
|
+
"prefix": None,
|
|
117
|
+
"ownedRelatedElement": de
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
if synthetic_relationships:
|
|
121
|
+
synthetic_definition = {
|
|
122
|
+
"name": "Package",
|
|
123
|
+
"declaration": {
|
|
124
|
+
"name": "PackageDeclaration",
|
|
125
|
+
"identification": {
|
|
126
|
+
"name": "Identification",
|
|
127
|
+
"declaredName": None,
|
|
128
|
+
"declaredShortName": None,
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"body": {
|
|
132
|
+
"name": "PackageBody",
|
|
133
|
+
"ownedRelationship": synthetic_relationships
|
|
134
|
+
},
|
|
135
|
+
"ownedRelationship": []
|
|
136
|
+
}
|
|
137
|
+
p = Package().load_from_grammar(
|
|
138
|
+
PackageGrammar(synthetic_definition)
|
|
139
|
+
)
|
|
140
|
+
self.children.append(p)
|
|
141
|
+
member_grammar.append(p._get_definition(child="PackageBody"))
|
|
142
|
+
else:
|
|
143
|
+
raise ValueError("Base Model must be encapsulated by a package.")
|
|
144
|
+
|
|
145
|
+
self.grammar = RootNamespace(
|
|
146
|
+
{"name": "PackageBodyElement", "ownedRelationship": member_grammar}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
def _ensure_body(self):
|
|
152
|
+
body = []
|
|
153
|
+
for abc in self.children:
|
|
154
|
+
v = abc._get_definition(child="PackageBody")
|
|
155
|
+
if isinstance(v, list):
|
|
156
|
+
for subchild in v:
|
|
157
|
+
body.append(PackageMember(subchild).get_definition())
|
|
158
|
+
else:
|
|
159
|
+
body.append(PackageMember(v).get_definition())
|
|
160
|
+
|
|
161
|
+
if hasattr(self, 'grammar') and self.grammar:
|
|
162
|
+
for child in self.grammar.children:
|
|
163
|
+
if child.__class__.__name__ in ('Import', 'AliasMember'):
|
|
164
|
+
body.append(child.get_definition())
|
|
165
|
+
|
|
166
|
+
if len(body) > 0:
|
|
167
|
+
self.grammar = RootNamespace(
|
|
168
|
+
{"name": "PackageBodyElement", "ownedRelationship": body}
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return self
|
|
172
|
+
|
|
173
|
+
def _get_definition(self):
|
|
174
|
+
return self.grammar.get_definition()
|
|
175
|
+
|
|
176
|
+
def dump(self):
|
|
177
|
+
if len(self.children) == 0:
|
|
178
|
+
raise ValueError("Base Model has no elements to output.")
|
|
179
|
+
|
|
180
|
+
self._ensure_body()
|
|
181
|
+
return classtree(self._get_definition()).dump()
|
|
182
|
+
|
|
183
|
+
def _set_child(self, child):
|
|
184
|
+
self.children.append(child)
|
|
185
|
+
child.parent = self
|
|
186
|
+
return self
|
|
187
|
+
|
|
188
|
+
def _get_child(self, featurechain):
|
|
189
|
+
# 'x.y.z'
|
|
190
|
+
if isinstance(featurechain, str):
|
|
191
|
+
fc = featurechain.split(".")
|
|
192
|
+
else:
|
|
193
|
+
raise TypeError
|
|
194
|
+
|
|
195
|
+
if fc[0] == self.name:
|
|
196
|
+
# This first one must match self name, otherwise pass it all
|
|
197
|
+
featurechain = ".".join(fc[1:])
|
|
198
|
+
|
|
199
|
+
for child in self.children:
|
|
200
|
+
fcs = featurechain.split(".")
|
|
201
|
+
if child.name == fcs[0]:
|
|
202
|
+
if len(fcs) == 1:
|
|
203
|
+
return child
|
|
204
|
+
else:
|
|
205
|
+
return child._get_child(featurechain)
|
|
206
|
+
|
|
207
|
+
# ── Convenience functions ───────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
def find_all(self, type=None, name=None):
|
|
210
|
+
"""Recursively find all matching elements across all packages.
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
----------
|
|
214
|
+
type : str or class, optional
|
|
215
|
+
Filter by SysML type (e.g. 'part', Part).
|
|
216
|
+
name : str, optional
|
|
217
|
+
Filter by element name.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
list
|
|
222
|
+
All matching elements across the full tree.
|
|
223
|
+
"""
|
|
224
|
+
results = []
|
|
225
|
+
for child in self.children:
|
|
226
|
+
if hasattr(child, "find_all"):
|
|
227
|
+
results.extend(child.find_all(type=type, name=name))
|
|
228
|
+
return results
|
|
229
|
+
|
|
230
|
+
def count(self, type=None):
|
|
231
|
+
"""Count elements by type across all packages.
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
type : str or class, optional
|
|
236
|
+
Count only this type. If None, returns a dict of all types.
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
int or dict
|
|
241
|
+
Count of matching elements, or dict of {type: count}.
|
|
242
|
+
"""
|
|
243
|
+
if type is not None:
|
|
244
|
+
return len(self.find_all(type=type))
|
|
245
|
+
|
|
246
|
+
counts = {}
|
|
247
|
+
for child in self.children:
|
|
248
|
+
if hasattr(child, "count"):
|
|
249
|
+
child_counts = child.count()
|
|
250
|
+
for k, v in child_counts.items():
|
|
251
|
+
counts[k] = counts.get(k, 0) + v
|
|
252
|
+
return counts
|
|
253
|
+
|
|
254
|
+
def traverse(self, callback, depth=0):
|
|
255
|
+
"""Walk all packages and their trees, calling callback for each element."""
|
|
256
|
+
for child in self.children:
|
|
257
|
+
if hasattr(child, "traverse"):
|
|
258
|
+
child.traverse(callback, depth)
|
|
259
|
+
|
|
260
|
+
def to_dict(self):
|
|
261
|
+
"""Export the model as a nested dictionary."""
|
|
262
|
+
return {
|
|
263
|
+
"name": "Model",
|
|
264
|
+
"children": [c.to_dict() if hasattr(c, "to_dict") else {"name": getattr(c, "name", None)} for c in self.children],
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
def to_graph(self):
|
|
268
|
+
"""Export the model as a NetworkX graph.
|
|
269
|
+
|
|
270
|
+
Requires: pip install networkx (or: pip install sysmlpy[graph])
|
|
271
|
+
"""
|
|
272
|
+
from sysmlpy.store import NetworkXStore, new_id
|
|
273
|
+
|
|
274
|
+
store = NetworkXStore()
|
|
275
|
+
|
|
276
|
+
def _add_element(elem, parent_id=None):
|
|
277
|
+
eid = new_id()
|
|
278
|
+
data = {
|
|
279
|
+
"name": getattr(elem, "name", None),
|
|
280
|
+
"sysml_type": getattr(elem, "sysml_type", None),
|
|
281
|
+
"python_type": type(elem).__name__,
|
|
282
|
+
}
|
|
283
|
+
store.put(eid, data, parent_id=parent_id)
|
|
284
|
+
if hasattr(elem, "children"):
|
|
285
|
+
for child in elem.children:
|
|
286
|
+
_add_element(child, eid)
|
|
287
|
+
|
|
288
|
+
for child in self.children:
|
|
289
|
+
_add_element(child)
|
|
290
|
+
return store
|
|
291
|
+
|
|
292
|
+
def path_between(self, source_name, target_name):
|
|
293
|
+
"""Find the path between two elements by name across all packages."""
|
|
294
|
+
for child in self.children:
|
|
295
|
+
if hasattr(child, "path_between"):
|
|
296
|
+
path = child.path_between(source_name, target_name)
|
|
297
|
+
if path is not None:
|
|
298
|
+
return path
|
|
299
|
+
return None
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class Package(Searchable):
|
|
303
|
+
"""SysML v2 Package. Exposes typed accessors and ``find()`` / ``all()``
|
|
304
|
+
across its direct and nested children."""
|
|
305
|
+
|
|
306
|
+
sysml_type = "package"
|
|
307
|
+
|
|
308
|
+
def __init__(self, name=None, shortname=None):
|
|
309
|
+
self.name = str(uuidlib.uuid4())
|
|
310
|
+
self.children = []
|
|
311
|
+
self.typedby = None
|
|
312
|
+
self.grammar = PackageGrammar()
|
|
313
|
+
self.parent = None
|
|
314
|
+
|
|
315
|
+
if name is not None:
|
|
316
|
+
self._set_name(name)
|
|
317
|
+
if shortname is not None:
|
|
318
|
+
self._set_name(shortname, short=True)
|
|
319
|
+
|
|
320
|
+
def __repr__(self):
|
|
321
|
+
name = getattr(self, 'name', None)
|
|
322
|
+
shortname = getattr(self.grammar.declaration.identification, 'declaredShortName', None)
|
|
323
|
+
|
|
324
|
+
cls_name = self.__class__.__name__
|
|
325
|
+
if name and shortname:
|
|
326
|
+
return f"{cls_name}(name={name!r}, shortname={shortname!r})"
|
|
327
|
+
elif name:
|
|
328
|
+
return f"{cls_name}(name={name!r})"
|
|
329
|
+
elif shortname:
|
|
330
|
+
return f"{cls_name}(shortname={shortname!r})"
|
|
331
|
+
else:
|
|
332
|
+
return f"{cls_name}()"
|
|
333
|
+
|
|
334
|
+
def _set_name(self, name, short=False):
|
|
335
|
+
if short:
|
|
336
|
+
if self.grammar.declaration.identification is None:
|
|
337
|
+
self.grammar.declaration.identification = Identification()
|
|
338
|
+
self.grammar.declaration.identification.declaredShortName = name
|
|
339
|
+
else:
|
|
340
|
+
self.name = name
|
|
341
|
+
if self.grammar.declaration.identification is None:
|
|
342
|
+
self.grammar.declaration.identification = Identification()
|
|
343
|
+
self.grammar.declaration.identification.declaredName = name
|
|
344
|
+
|
|
345
|
+
return self
|
|
346
|
+
|
|
347
|
+
def _get_name(self):
|
|
348
|
+
return self.grammar.declaration.identification.declaredName
|
|
349
|
+
|
|
350
|
+
def _set_child(self, child):
|
|
351
|
+
self.children.append(child)
|
|
352
|
+
child.parent = self
|
|
353
|
+
return self
|
|
354
|
+
|
|
355
|
+
def _get_child(self, featurechain):
|
|
356
|
+
# 'x.y.z'
|
|
357
|
+
if isinstance(featurechain, str):
|
|
358
|
+
fc = featurechain.split(".")
|
|
359
|
+
else:
|
|
360
|
+
raise TypeError
|
|
361
|
+
|
|
362
|
+
if fc[0] == self.name:
|
|
363
|
+
# This first one must match self name, otherwise pass it all
|
|
364
|
+
featurechain = ".".join(fc[1:])
|
|
365
|
+
|
|
366
|
+
for child in self.children:
|
|
367
|
+
fcs = featurechain.split(".")
|
|
368
|
+
if child.name == fcs[0]:
|
|
369
|
+
if len(fcs) == 1:
|
|
370
|
+
return child
|
|
371
|
+
else:
|
|
372
|
+
return child._get_child(featurechain)
|
|
373
|
+
|
|
374
|
+
def _ensure_body(self):
|
|
375
|
+
body = []
|
|
376
|
+
for abc in self.children:
|
|
377
|
+
v = abc._get_definition(child="PackageBody")
|
|
378
|
+
if isinstance(v, list):
|
|
379
|
+
for subchild in v:
|
|
380
|
+
body.append(PackageMember(subchild).get_definition())
|
|
381
|
+
else:
|
|
382
|
+
body.append(PackageMember(v).get_definition())
|
|
383
|
+
|
|
384
|
+
if hasattr(self.grammar, 'body') and self.grammar.body:
|
|
385
|
+
for child in self.grammar.body.children:
|
|
386
|
+
if child.__class__.__name__ in ('Import', 'AliasMember'):
|
|
387
|
+
body.append(child.get_definition())
|
|
388
|
+
|
|
389
|
+
if len(body) > 0:
|
|
390
|
+
self.grammar.body = PackageBody(
|
|
391
|
+
{"name": "PackageBody", "ownedRelationship": body}
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
def _get_definition(self, child=None):
|
|
395
|
+
self._ensure_body()
|
|
396
|
+
|
|
397
|
+
package = {
|
|
398
|
+
"name": "DefinitionElement",
|
|
399
|
+
"ownedRelatedElement": self.grammar.get_definition(),
|
|
400
|
+
}
|
|
401
|
+
package = {
|
|
402
|
+
"name": "PackageMember",
|
|
403
|
+
"ownedRelatedElement": package,
|
|
404
|
+
"prefix": None,
|
|
405
|
+
}
|
|
406
|
+
if not child:
|
|
407
|
+
package = {
|
|
408
|
+
"name": "PackageBodyElement",
|
|
409
|
+
"ownedRelationship": [package],
|
|
410
|
+
"prefix": None,
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Add the typed by definition to the package output
|
|
414
|
+
if self.typedby is not None:
|
|
415
|
+
# Packages cannot be typed, they should import from other packages
|
|
416
|
+
raise NotImplementedError
|
|
417
|
+
|
|
418
|
+
return package
|
|
419
|
+
|
|
420
|
+
def dump(self, child=None):
|
|
421
|
+
return classtree(self._get_definition(child=False)).dump()
|
|
422
|
+
|
|
423
|
+
def load_from_grammar(self, grammar):
|
|
424
|
+
# Get the identification values
|
|
425
|
+
declared_name = grammar.declaration.identification.declaredName
|
|
426
|
+
declared_shortname = grammar.declaration.identification.declaredShortName if hasattr(grammar.declaration.identification, 'declaredShortName') else None
|
|
427
|
+
|
|
428
|
+
# Handle the different cases:
|
|
429
|
+
if declared_shortname and declared_name:
|
|
430
|
+
# Both short and long name provided
|
|
431
|
+
self.name = declared_name
|
|
432
|
+
self._set_name(declared_shortname, short=True)
|
|
433
|
+
elif declared_shortname and not declared_name:
|
|
434
|
+
# Only short name is provided - use it as name
|
|
435
|
+
self.name = declared_shortname
|
|
436
|
+
self._set_name(declared_shortname, short=True)
|
|
437
|
+
elif declared_name:
|
|
438
|
+
# Only long name provided
|
|
439
|
+
self.name = declared_name
|
|
440
|
+
|
|
441
|
+
self.grammar = grammar
|
|
442
|
+
for child in grammar.body.children:
|
|
443
|
+
# Navigate the nested structure:
|
|
444
|
+
# DefinitionBodyItem -> NonOccurrenceUsageMember -> NonOccurrenceUsageElement -> StructureUsageElement/BehaviorUsageElement
|
|
445
|
+
# Or: DefinitionBodyItem -> PackageMember -> DefinitionElement -> ItemDefinition/PartDefinition
|
|
446
|
+
# Or: DefinitionBodyItem -> PackageMember -> UsageElement -> OccurrenceUsageElement -> StructureUsageElement -> ItemUsage/PartUsage
|
|
447
|
+
# Or: PackageBody -> UsageElement directly (for analysis cases in packages)
|
|
448
|
+
if not hasattr(child, 'children') or not child.children:
|
|
449
|
+
continue
|
|
450
|
+
|
|
451
|
+
# Handle case where child.children is a single object (not a list)
|
|
452
|
+
if isinstance(child.children, list):
|
|
453
|
+
first_child = child.children[0]
|
|
454
|
+
else:
|
|
455
|
+
first_child = child.children
|
|
456
|
+
|
|
457
|
+
# Handle NonOccurrenceUsageMember (for usages like item, attribute)
|
|
458
|
+
if first_child.__class__.__name__ == 'NonOccurrenceUsageMember':
|
|
459
|
+
if hasattr(first_child, 'children') and first_child.children:
|
|
460
|
+
second = first_child.children[0]
|
|
461
|
+
if hasattr(second, 'children'):
|
|
462
|
+
# NonOccurrenceUsageElement -> StructureUsageElement or BehaviorUsageElement
|
|
463
|
+
inner_element = second.children
|
|
464
|
+
else:
|
|
465
|
+
inner_element = second
|
|
466
|
+
else:
|
|
467
|
+
continue
|
|
468
|
+
# Handle DefinitionElement (for definitions like item def, part def)
|
|
469
|
+
elif first_child.__class__.__name__ == 'DefinitionElement':
|
|
470
|
+
if hasattr(first_child, 'children') and first_child.children:
|
|
471
|
+
inner_element = first_child.children[0]
|
|
472
|
+
else:
|
|
473
|
+
continue
|
|
474
|
+
# Handle UsageElement (for usages like item Hydrogen : Fuel, action Act1)
|
|
475
|
+
elif first_child.__class__.__name__ == 'UsageElement':
|
|
476
|
+
if hasattr(first_child, 'children') and first_child.children:
|
|
477
|
+
# Navigate: UsageElement -> OccurrenceUsageElement/NonOccurrenceUsageElement -> Usage
|
|
478
|
+
# first_child.children can be an object (not a list)
|
|
479
|
+
occ = first_child.children
|
|
480
|
+
|
|
481
|
+
# Check if it's OccurrenceUsageElement (has children containing Structure/Behavior element)
|
|
482
|
+
if hasattr(occ, 'children') and occ.children:
|
|
483
|
+
inner = occ.children
|
|
484
|
+
# Check if it's StructureUsageElement or BehaviorUsageElement
|
|
485
|
+
if hasattr(inner, 'children'):
|
|
486
|
+
inner_element = inner.children
|
|
487
|
+
else:
|
|
488
|
+
inner_element = inner
|
|
489
|
+
# Check if it's NonOccurrenceUsageElement
|
|
490
|
+
elif hasattr(occ, 'ownedRelationship') or hasattr(occ, 'declaration'):
|
|
491
|
+
inner_element = occ
|
|
492
|
+
else:
|
|
493
|
+
continue
|
|
494
|
+
else:
|
|
495
|
+
continue
|
|
496
|
+
else:
|
|
497
|
+
continue
|
|
498
|
+
|
|
499
|
+
inner_class = inner_element.__class__.__name__
|
|
500
|
+
|
|
501
|
+
if inner_class == "ItemUsage":
|
|
502
|
+
child = Item().load_from_grammar(inner_element)
|
|
503
|
+
child.parent = self
|
|
504
|
+
self.children.append(child)
|
|
505
|
+
elif inner_class == "PartUsage":
|
|
506
|
+
child = Part().load_from_grammar(inner_element)
|
|
507
|
+
child.parent = self
|
|
508
|
+
self.children.append(child)
|
|
509
|
+
elif inner_class == "PortUsage":
|
|
510
|
+
child = Port().load_from_grammar(inner_element)
|
|
511
|
+
child.parent = self
|
|
512
|
+
self.children.append(child)
|
|
513
|
+
elif inner_class == "Package":
|
|
514
|
+
child = Package().load_from_grammar(inner_element)
|
|
515
|
+
child.parent = self
|
|
516
|
+
self.children.append(child)
|
|
517
|
+
elif inner_class == "ItemDefinition":
|
|
518
|
+
child = Item().load_from_grammar(inner_element)
|
|
519
|
+
child.parent = self
|
|
520
|
+
self.children.append(child)
|
|
521
|
+
elif inner_class == "PartDefinition":
|
|
522
|
+
child = Part(definition=True).load_from_grammar(inner_element)
|
|
523
|
+
child.parent = self
|
|
524
|
+
self.children.append(child)
|
|
525
|
+
elif inner_class == "PortDefinition":
|
|
526
|
+
child = Port(definition=True).load_from_grammar(inner_element)
|
|
527
|
+
child.parent = self
|
|
528
|
+
self.children.append(child)
|
|
529
|
+
elif inner_class == "AttributeDefinition":
|
|
530
|
+
child = Attribute(definition=True).load_from_grammar(inner_element)
|
|
531
|
+
child.parent = self
|
|
532
|
+
self.children.append(child)
|
|
533
|
+
elif inner_class == "AttributeUsage":
|
|
534
|
+
child = Attribute().load_from_grammar(inner_element)
|
|
535
|
+
child.parent = self
|
|
536
|
+
self.children.append(child)
|
|
537
|
+
elif inner_class == "ActionDefinition":
|
|
538
|
+
child = Action(definition=True).load_from_grammar(inner_element)
|
|
539
|
+
child.parent = self
|
|
540
|
+
self.children.append(child)
|
|
541
|
+
elif inner_class == "RequirementDefinition":
|
|
542
|
+
child = Requirement(definition=True).load_from_grammar(inner_element)
|
|
543
|
+
child.parent = self
|
|
544
|
+
self.children.append(child)
|
|
545
|
+
elif inner_class == "UseCaseDefinition":
|
|
546
|
+
child = UseCase(definition=True).load_from_grammar(inner_element)
|
|
547
|
+
child.parent = self
|
|
548
|
+
self.children.append(child)
|
|
549
|
+
elif inner_class == "ActionUsage":
|
|
550
|
+
child = Action(grammar=inner_element).load_from_grammar(inner_element)
|
|
551
|
+
child.parent = self
|
|
552
|
+
self.children.append(child)
|
|
553
|
+
elif inner_class == "ActionUsage":
|
|
554
|
+
child = Action().load_from_grammar(inner_element)
|
|
555
|
+
child.parent = self
|
|
556
|
+
self.children.append(child)
|
|
557
|
+
elif inner_class == "ConstraintDefinition":
|
|
558
|
+
c = Constraint(definition=True)
|
|
559
|
+
c.grammar = inner_element
|
|
560
|
+
# Extract the name from the grammar
|
|
561
|
+
if hasattr(inner_element, 'declaration') and hasattr(inner_element.declaration, 'identification') and inner_element.declaration.identification:
|
|
562
|
+
c.name = inner_element.declaration.identification.declaredName
|
|
563
|
+
c.parent = self
|
|
564
|
+
self.children.append(c)
|
|
565
|
+
elif inner_class == "ConstraintUsage":
|
|
566
|
+
c = Constraint()
|
|
567
|
+
c.grammar = inner_element
|
|
568
|
+
# Navigate to get name from nested declaration structure
|
|
569
|
+
try:
|
|
570
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
571
|
+
decl = inner_element.declaration
|
|
572
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
573
|
+
inner_decl = decl.declaration
|
|
574
|
+
if hasattr(inner_decl, 'declaration') and inner_decl.declaration:
|
|
575
|
+
feat_decl = inner_decl.declaration
|
|
576
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
577
|
+
c.name = feat_decl.identification.declaredName
|
|
578
|
+
except AttributeError:
|
|
579
|
+
pass
|
|
580
|
+
c.parent = self
|
|
581
|
+
self.children.append(c)
|
|
582
|
+
elif inner_class == "StateDefinition":
|
|
583
|
+
s = State(definition=True).load_from_grammar(inner_element)
|
|
584
|
+
s.parent = self
|
|
585
|
+
self.children.append(s)
|
|
586
|
+
elif inner_class == "StateUsage":
|
|
587
|
+
s = State().load_from_grammar(inner_element)
|
|
588
|
+
s.parent = self
|
|
589
|
+
self.children.append(s)
|
|
590
|
+
elif inner_class == "CalculationDefinition":
|
|
591
|
+
c = Calculation(definition=True)
|
|
592
|
+
c.grammar = inner_element
|
|
593
|
+
if hasattr(inner_element, 'declaration') and hasattr(inner_element.declaration, 'identification') and inner_element.declaration.identification:
|
|
594
|
+
c.name = inner_element.declaration.identification.declaredName
|
|
595
|
+
c.parent = self
|
|
596
|
+
self.children.append(c)
|
|
597
|
+
elif inner_class == "CalculationUsage":
|
|
598
|
+
c = Calculation()
|
|
599
|
+
c.grammar = inner_element
|
|
600
|
+
try:
|
|
601
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
602
|
+
decl = inner_element.declaration
|
|
603
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
604
|
+
inner_decl = decl.declaration
|
|
605
|
+
if hasattr(inner_decl, 'declaration') and inner_decl.declaration:
|
|
606
|
+
feat_decl = inner_decl.declaration
|
|
607
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
608
|
+
c.name = feat_decl.identification.declaredName
|
|
609
|
+
except AttributeError:
|
|
610
|
+
pass
|
|
611
|
+
c.parent = self
|
|
612
|
+
self.children.append(c)
|
|
613
|
+
elif inner_class == "ConnectionDefinition":
|
|
614
|
+
c = Connection(definition=True)
|
|
615
|
+
c.grammar = inner_element
|
|
616
|
+
if hasattr(inner_element, 'definition') and hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
617
|
+
decl = inner_element.definition.declaration
|
|
618
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
619
|
+
c.name = decl.identification.declaredName
|
|
620
|
+
c.parent = self
|
|
621
|
+
self.children.append(c)
|
|
622
|
+
elif inner_class == "ConnectionUsage":
|
|
623
|
+
c = Connection()
|
|
624
|
+
c.grammar = inner_element
|
|
625
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
626
|
+
decl = inner_element.declaration
|
|
627
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
628
|
+
feat_decl = decl.declaration
|
|
629
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
630
|
+
c.name = feat_decl.identification.declaredName
|
|
631
|
+
c.parent = self
|
|
632
|
+
self.children.append(c)
|
|
633
|
+
elif inner_class == "FlowConnectionDefinition":
|
|
634
|
+
f = Flow(definition=True)
|
|
635
|
+
f.grammar = inner_element
|
|
636
|
+
if hasattr(inner_element, 'definition') and hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
637
|
+
decl = inner_element.definition.declaration
|
|
638
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
639
|
+
f.name = decl.identification.declaredName
|
|
640
|
+
f.parent = self
|
|
641
|
+
self.children.append(f)
|
|
642
|
+
elif inner_class == "FlowConnectionUsage":
|
|
643
|
+
f = Flow()
|
|
644
|
+
f.grammar = inner_element
|
|
645
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
646
|
+
decl = inner_element.declaration
|
|
647
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
648
|
+
feat_decl = decl.declaration
|
|
649
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
650
|
+
f.name = feat_decl.identification.declaredName
|
|
651
|
+
f.parent = self
|
|
652
|
+
self.children.append(f)
|
|
653
|
+
elif inner_class == "EnumerationDefinition":
|
|
654
|
+
e = Enumeration(name=None) # will set below
|
|
655
|
+
e.grammar = inner_element
|
|
656
|
+
if hasattr(inner_element, 'declaration') and hasattr(inner_element.declaration, 'identification') and inner_element.declaration.identification:
|
|
657
|
+
e.name = inner_element.declaration.identification.declaredName
|
|
658
|
+
e.parent = self
|
|
659
|
+
self.children.append(e)
|
|
660
|
+
elif inner_class == "AllocationDefinition":
|
|
661
|
+
a = Allocation(definition=True)
|
|
662
|
+
a.grammar = inner_element
|
|
663
|
+
# AllocationDefinition uses 'definition' -> Definition -> DefinitionDeclaration
|
|
664
|
+
if hasattr(inner_element, 'definition') and inner_element.definition:
|
|
665
|
+
if hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
666
|
+
decl = inner_element.definition.declaration
|
|
667
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
668
|
+
a.name = decl.identification.declaredName
|
|
669
|
+
a.parent = self
|
|
670
|
+
self.children.append(a)
|
|
671
|
+
elif inner_class == "AllocationUsage":
|
|
672
|
+
a = Allocation()
|
|
673
|
+
a.grammar = inner_element
|
|
674
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
675
|
+
decl = inner_element.declaration
|
|
676
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
677
|
+
feat_decl = decl.declaration
|
|
678
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
679
|
+
a.name = feat_decl.identification.declaredName
|
|
680
|
+
a.parent = self
|
|
681
|
+
self.children.append(a)
|
|
682
|
+
elif inner_class == "MetadataDefinition":
|
|
683
|
+
m = Metadata(definition=True)
|
|
684
|
+
m.grammar = inner_element
|
|
685
|
+
if hasattr(inner_element, 'definition') and inner_element.definition:
|
|
686
|
+
if hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
687
|
+
decl = inner_element.definition.declaration
|
|
688
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
689
|
+
m.name = decl.identification.declaredName
|
|
690
|
+
m.parent = self
|
|
691
|
+
self.children.append(m)
|
|
692
|
+
elif inner_class == "MetadataUsage":
|
|
693
|
+
m = Metadata()
|
|
694
|
+
m.grammar = inner_element
|
|
695
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
696
|
+
decl = inner_element.declaration
|
|
697
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
698
|
+
feat_decl = decl.declaration
|
|
699
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
700
|
+
m.name = feat_decl.identification.declaredName
|
|
701
|
+
m.parent = self
|
|
702
|
+
self.children.append(m)
|
|
703
|
+
elif inner_class == "RenderingDefinition":
|
|
704
|
+
r = Rendering(definition=True)
|
|
705
|
+
r.grammar = inner_element
|
|
706
|
+
if hasattr(inner_element, 'definition') and inner_element.definition:
|
|
707
|
+
if hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
708
|
+
decl = inner_element.definition.declaration
|
|
709
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
710
|
+
r.name = decl.identification.declaredName
|
|
711
|
+
r.parent = self
|
|
712
|
+
self.children.append(r)
|
|
713
|
+
elif inner_class == "RenderingUsage":
|
|
714
|
+
r = Rendering()
|
|
715
|
+
r.grammar = inner_element
|
|
716
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
717
|
+
decl = inner_element.declaration
|
|
718
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
719
|
+
feat_decl = decl.declaration
|
|
720
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
721
|
+
r.name = feat_decl.identification.declaredName
|
|
722
|
+
r.parent = self
|
|
723
|
+
self.children.append(r)
|
|
724
|
+
elif inner_class == "IndividualDefinition":
|
|
725
|
+
i = Individual(definition=True)
|
|
726
|
+
i.grammar = inner_element
|
|
727
|
+
if hasattr(inner_element, 'definition') and inner_element.definition:
|
|
728
|
+
if hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
729
|
+
decl = inner_element.definition.declaration
|
|
730
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
731
|
+
i.name = decl.identification.declaredName
|
|
732
|
+
i.parent = self
|
|
733
|
+
self.children.append(i)
|
|
734
|
+
elif inner_class == "IndividualUsage" or inner_class == "IndividualUsageSimple":
|
|
735
|
+
i = Individual()
|
|
736
|
+
i.grammar = inner_element
|
|
737
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
738
|
+
decl = inner_element.declaration
|
|
739
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
740
|
+
feat_decl = decl.declaration
|
|
741
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
742
|
+
i.name = feat_decl.identification.declaredName
|
|
743
|
+
i.parent = self
|
|
744
|
+
self.children.append(i)
|
|
745
|
+
elif inner_class == "FlowDefinition":
|
|
746
|
+
f = FlowDef(name=None)
|
|
747
|
+
f.grammar = inner_element
|
|
748
|
+
if hasattr(inner_element, 'definition') and inner_element.definition:
|
|
749
|
+
if hasattr(inner_element.definition, 'declaration') and inner_element.definition.declaration:
|
|
750
|
+
decl = inner_element.definition.declaration
|
|
751
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
752
|
+
f.name = decl.identification.declaredName
|
|
753
|
+
f.parent = self
|
|
754
|
+
self.children.append(f)
|
|
755
|
+
elif inner_class == "ViewDefinition":
|
|
756
|
+
v = View(definition=True)
|
|
757
|
+
v.grammar = inner_element
|
|
758
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
759
|
+
decl = inner_element.declaration
|
|
760
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
761
|
+
v.name = decl.identification.declaredName
|
|
762
|
+
v.parent = self
|
|
763
|
+
self.children.append(v)
|
|
764
|
+
elif inner_class == "ViewUsage":
|
|
765
|
+
v = View()
|
|
766
|
+
v.grammar = inner_element
|
|
767
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
768
|
+
decl = inner_element.declaration
|
|
769
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
770
|
+
feat_decl = decl.declaration
|
|
771
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
772
|
+
v.name = feat_decl.identification.declaredName
|
|
773
|
+
v.parent = self
|
|
774
|
+
self.children.append(v)
|
|
775
|
+
elif inner_class == "ViewpointDefinition":
|
|
776
|
+
vp = Viewpoint(definition=True)
|
|
777
|
+
vp.grammar = inner_element
|
|
778
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
779
|
+
decl = inner_element.declaration
|
|
780
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
781
|
+
vp.name = decl.identification.declaredName
|
|
782
|
+
vp.parent = self
|
|
783
|
+
self.children.append(vp)
|
|
784
|
+
elif inner_class == "ViewpointUsage":
|
|
785
|
+
vp = Viewpoint()
|
|
786
|
+
vp.grammar = inner_element
|
|
787
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
788
|
+
decl = inner_element.declaration
|
|
789
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
790
|
+
feat_decl = decl.declaration
|
|
791
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
792
|
+
vp.name = feat_decl.identification.declaredName
|
|
793
|
+
vp.parent = self
|
|
794
|
+
self.children.append(vp)
|
|
795
|
+
elif inner_class == "ConcernDefinition":
|
|
796
|
+
c = Concern(definition=True)
|
|
797
|
+
c.grammar = inner_element
|
|
798
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
799
|
+
decl = inner_element.declaration
|
|
800
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
801
|
+
c.name = decl.identification.declaredName
|
|
802
|
+
c.parent = self
|
|
803
|
+
self.children.append(c)
|
|
804
|
+
elif inner_class == "ConcernUsage":
|
|
805
|
+
c = Concern()
|
|
806
|
+
c.grammar = inner_element
|
|
807
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
808
|
+
decl = inner_element.declaration
|
|
809
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
810
|
+
feat_decl = decl.declaration
|
|
811
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
812
|
+
c.name = feat_decl.identification.declaredName
|
|
813
|
+
c.parent = self
|
|
814
|
+
self.children.append(c)
|
|
815
|
+
elif inner_class == "CaseDefinition":
|
|
816
|
+
c = Case(definition=True)
|
|
817
|
+
c.grammar = inner_element
|
|
818
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
819
|
+
decl = inner_element.declaration
|
|
820
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
821
|
+
c.name = decl.identification.declaredName
|
|
822
|
+
c.parent = self
|
|
823
|
+
self.children.append(c)
|
|
824
|
+
elif inner_class == "AnalysisCaseDefinition":
|
|
825
|
+
c = AnalysisCase(definition=True)
|
|
826
|
+
c.grammar = inner_element
|
|
827
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
828
|
+
decl = inner_element.declaration
|
|
829
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
830
|
+
c.name = decl.identification.declaredName
|
|
831
|
+
c.parent = self
|
|
832
|
+
self.children.append(c)
|
|
833
|
+
elif inner_class == "VerificationCaseDefinition":
|
|
834
|
+
c = VerificationCase(definition=True)
|
|
835
|
+
c.grammar = inner_element
|
|
836
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
837
|
+
decl = inner_element.declaration
|
|
838
|
+
if hasattr(decl, 'identification') and decl.identification:
|
|
839
|
+
c.name = decl.identification.declaredName
|
|
840
|
+
c.parent = self
|
|
841
|
+
self.children.append(c)
|
|
842
|
+
elif inner_class == "AnnotatingElement":
|
|
843
|
+
wrapper = _GrammarAnnotationWrapper(inner_element)
|
|
844
|
+
wrapper.parent = self
|
|
845
|
+
self.children.append(wrapper)
|
|
846
|
+
elif inner_class == "InterfaceDefinition":
|
|
847
|
+
wrapper = _GrammarAnnotationWrapper(inner_element)
|
|
848
|
+
wrapper.parent = self
|
|
849
|
+
self.children.append(wrapper)
|
|
850
|
+
elif inner_class == "InterfaceUsage":
|
|
851
|
+
wrapper = _GrammarAnnotationWrapper(inner_element)
|
|
852
|
+
wrapper.parent = self
|
|
853
|
+
self.children.append(wrapper)
|
|
854
|
+
elif inner_class == "AssertConstraintUsage":
|
|
855
|
+
c = Constraint()
|
|
856
|
+
c.grammar = inner_element
|
|
857
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
858
|
+
decl = inner_element.declaration
|
|
859
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
860
|
+
feat_decl = decl.declaration
|
|
861
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
862
|
+
c.name = feat_decl.identification.declaredName
|
|
863
|
+
c.parent = self
|
|
864
|
+
self.children.append(c)
|
|
865
|
+
elif inner_class == "RequirementUsage":
|
|
866
|
+
r = Requirement()
|
|
867
|
+
r.grammar = inner_element
|
|
868
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
869
|
+
decl = inner_element.declaration
|
|
870
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
871
|
+
feat_decl = decl.declaration
|
|
872
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
873
|
+
r.name = feat_decl.identification.declaredName
|
|
874
|
+
r.parent = self
|
|
875
|
+
self.children.append(r)
|
|
876
|
+
elif inner_class == "SatisfyRequirementUsage":
|
|
877
|
+
r = Requirement()
|
|
878
|
+
r.grammar = inner_element
|
|
879
|
+
if hasattr(inner_element, 'declaration') and inner_element.declaration:
|
|
880
|
+
decl = inner_element.declaration
|
|
881
|
+
if hasattr(decl, 'declaration') and decl.declaration:
|
|
882
|
+
feat_decl = decl.declaration
|
|
883
|
+
if hasattr(feat_decl, 'identification') and feat_decl.identification:
|
|
884
|
+
r.name = feat_decl.identification.declaredName
|
|
885
|
+
r.parent = self
|
|
886
|
+
self.children.append(r)
|
|
887
|
+
else:
|
|
888
|
+
print(f"Unknown class: {inner_class}")
|
|
889
|
+
raise NotImplementedError
|
|
890
|
+
|
|
891
|
+
return self
|
|
892
|
+
|
|
893
|
+
def _get_grammar(self):
|
|
894
|
+
# Force updates to grammar if something has changed.
|
|
895
|
+
self._ensure_body()
|
|
896
|
+
return self.grammar
|
|
897
|
+
|
|
898
|
+
# ── Convenience functions ───────────────────────────────────────────
|
|
899
|
+
|
|
900
|
+
def find_all(self, type=None, name=None):
|
|
901
|
+
"""Recursively find all matching elements in this package and children.
|
|
902
|
+
|
|
903
|
+
Parameters
|
|
904
|
+
----------
|
|
905
|
+
type : str or class, optional
|
|
906
|
+
Filter by SysML type (e.g. 'part', Part).
|
|
907
|
+
name : str, optional
|
|
908
|
+
Filter by element name.
|
|
909
|
+
|
|
910
|
+
Returns
|
|
911
|
+
-------
|
|
912
|
+
list
|
|
913
|
+
All matching elements across the full tree.
|
|
914
|
+
"""
|
|
915
|
+
results = []
|
|
916
|
+
for child in self.children:
|
|
917
|
+
match = True
|
|
918
|
+
if name is not None and getattr(child, "name", None) != name:
|
|
919
|
+
match = False
|
|
920
|
+
if type is not None:
|
|
921
|
+
child_type = getattr(child, "sysml_type", None)
|
|
922
|
+
if isinstance(type, str):
|
|
923
|
+
if child_type != type:
|
|
924
|
+
match = False
|
|
925
|
+
else:
|
|
926
|
+
if not isinstance(child, type):
|
|
927
|
+
match = False
|
|
928
|
+
if match:
|
|
929
|
+
results.append(child)
|
|
930
|
+
if hasattr(child, "find_all"):
|
|
931
|
+
results.extend(child.find_all(type=type, name=name))
|
|
932
|
+
return results
|
|
933
|
+
|
|
934
|
+
def count(self, type=None):
|
|
935
|
+
"""Count elements by type across the full tree.
|
|
936
|
+
|
|
937
|
+
Parameters
|
|
938
|
+
----------
|
|
939
|
+
type : str or class, optional
|
|
940
|
+
Count only this type. If None, returns a dict of all types.
|
|
941
|
+
|
|
942
|
+
Returns
|
|
943
|
+
-------
|
|
944
|
+
int or dict
|
|
945
|
+
Count of matching elements, or dict of {type: count}.
|
|
946
|
+
"""
|
|
947
|
+
if type is not None:
|
|
948
|
+
return len(self.find_all(type=type))
|
|
949
|
+
|
|
950
|
+
counts = {}
|
|
951
|
+
for child in self.children:
|
|
952
|
+
t = getattr(child, "sysml_type", "unknown")
|
|
953
|
+
counts[t] = counts.get(t, 0) + 1
|
|
954
|
+
if hasattr(child, "count"):
|
|
955
|
+
child_counts = child.count()
|
|
956
|
+
for k, v in child_counts.items():
|
|
957
|
+
counts[k] = counts.get(k, 0) + v
|
|
958
|
+
return counts
|
|
959
|
+
|
|
960
|
+
def traverse(self, callback, depth=0):
|
|
961
|
+
"""Walk the element tree, calling callback for each element.
|
|
962
|
+
|
|
963
|
+
Parameters
|
|
964
|
+
----------
|
|
965
|
+
callback : callable
|
|
966
|
+
Called as callback(element, depth) for each element.
|
|
967
|
+
depth : int
|
|
968
|
+
Current depth level (internal use).
|
|
969
|
+
"""
|
|
970
|
+
for child in self.children:
|
|
971
|
+
callback(child, depth)
|
|
972
|
+
if hasattr(child, "traverse"):
|
|
973
|
+
child.traverse(callback, depth + 1)
|
|
974
|
+
|
|
975
|
+
def to_dict(self):
|
|
976
|
+
"""Export the package and all children as a nested dictionary.
|
|
977
|
+
|
|
978
|
+
Returns
|
|
979
|
+
-------
|
|
980
|
+
dict
|
|
981
|
+
Nested dict representation of the package tree.
|
|
982
|
+
"""
|
|
983
|
+
result = {
|
|
984
|
+
"name": getattr(self, "name", None),
|
|
985
|
+
"sysml_type": getattr(self, "sysml_type", None),
|
|
986
|
+
"children": [],
|
|
987
|
+
}
|
|
988
|
+
for child in self.children:
|
|
989
|
+
if hasattr(child, "to_dict"):
|
|
990
|
+
result["children"].append(child.to_dict())
|
|
991
|
+
else:
|
|
992
|
+
result["children"].append({
|
|
993
|
+
"name": getattr(child, "name", None),
|
|
994
|
+
"sysml_type": getattr(child, "sysml_type", None),
|
|
995
|
+
})
|
|
996
|
+
return result
|
|
997
|
+
|
|
998
|
+
def to_graph(self):
|
|
999
|
+
"""Export the package tree as a NetworkX graph.
|
|
1000
|
+
|
|
1001
|
+
Requires: pip install networkx (or: pip install sysmlpy[graph])
|
|
1002
|
+
|
|
1003
|
+
Returns
|
|
1004
|
+
-------
|
|
1005
|
+
NetworkXStore
|
|
1006
|
+
A graph store with elements as nodes and parent-child
|
|
1007
|
+
relationships as edges.
|
|
1008
|
+
"""
|
|
1009
|
+
from sysmlpy.store import NetworkXStore, new_id, REL_PARENT_CHILD
|
|
1010
|
+
|
|
1011
|
+
store = NetworkXStore()
|
|
1012
|
+
id_map = {}
|
|
1013
|
+
|
|
1014
|
+
def _add_element(elem, parent_id=None):
|
|
1015
|
+
eid = new_id()
|
|
1016
|
+
id_map[id(elem)] = eid
|
|
1017
|
+
data = {
|
|
1018
|
+
"name": getattr(elem, "name", None),
|
|
1019
|
+
"sysml_type": getattr(elem, "sysml_type", None),
|
|
1020
|
+
"python_type": type(elem).__name__,
|
|
1021
|
+
}
|
|
1022
|
+
store.put(eid, data, parent_id=parent_id)
|
|
1023
|
+
if hasattr(elem, "children"):
|
|
1024
|
+
for child in elem.children:
|
|
1025
|
+
child_id = _add_element(child, eid)
|
|
1026
|
+
return eid
|
|
1027
|
+
|
|
1028
|
+
_add_element(self)
|
|
1029
|
+
return store
|
|
1030
|
+
|
|
1031
|
+
def path_between(self, source_name, target_name):
|
|
1032
|
+
"""Find the path between two elements by name.
|
|
1033
|
+
|
|
1034
|
+
Parameters
|
|
1035
|
+
----------
|
|
1036
|
+
source_name : str
|
|
1037
|
+
Name of the source element.
|
|
1038
|
+
target_name : str
|
|
1039
|
+
Name of the target element.
|
|
1040
|
+
|
|
1041
|
+
Returns
|
|
1042
|
+
-------
|
|
1043
|
+
list or None
|
|
1044
|
+
List of element names forming the path, or None if no path exists.
|
|
1045
|
+
"""
|
|
1046
|
+
source = self.find(name=source_name, recursive=True)
|
|
1047
|
+
target = self.find(name=target_name, recursive=True)
|
|
1048
|
+
if source is None or target is None:
|
|
1049
|
+
return None
|
|
1050
|
+
# find() returns a list
|
|
1051
|
+
if isinstance(source, list):
|
|
1052
|
+
source = source[0] if source else None
|
|
1053
|
+
if isinstance(target, list):
|
|
1054
|
+
target = target[0] if target else None
|
|
1055
|
+
if source is None or target is None:
|
|
1056
|
+
return None
|
|
1057
|
+
|
|
1058
|
+
# Build parent chain for both
|
|
1059
|
+
def parent_chain(elem):
|
|
1060
|
+
chain = [elem]
|
|
1061
|
+
current = elem
|
|
1062
|
+
while hasattr(current, "parent") and current.parent is not None:
|
|
1063
|
+
chain.append(current.parent)
|
|
1064
|
+
current = current.parent
|
|
1065
|
+
return chain
|
|
1066
|
+
|
|
1067
|
+
source_chain = parent_chain(source)
|
|
1068
|
+
target_chain = parent_chain(target)
|
|
1069
|
+
|
|
1070
|
+
# Find common ancestor
|
|
1071
|
+
source_set = {id(e): e for e in source_chain}
|
|
1072
|
+
for e in target_chain:
|
|
1073
|
+
if id(e) in source_set:
|
|
1074
|
+
common = e
|
|
1075
|
+
break
|
|
1076
|
+
else:
|
|
1077
|
+
return None
|
|
1078
|
+
|
|
1079
|
+
# Build path: source -> ... -> common -> ... -> target
|
|
1080
|
+
path = []
|
|
1081
|
+
current = source
|
|
1082
|
+
while current is not common:
|
|
1083
|
+
path.append(getattr(current, "name", "?"))
|
|
1084
|
+
current = getattr(current, "parent", None)
|
|
1085
|
+
path.append(getattr(common, "name", "?"))
|
|
1086
|
+
|
|
1087
|
+
target_to_common = []
|
|
1088
|
+
current = target
|
|
1089
|
+
while current is not common:
|
|
1090
|
+
target_to_common.append(getattr(current, "name", "?"))
|
|
1091
|
+
current = getattr(current, "parent", None)
|
|
1092
|
+
path.extend(reversed(target_to_common))
|
|
1093
|
+
|
|
1094
|
+
return path
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
class _GrammarAnnotationWrapper:
|
|
1098
|
+
"""Minimal wrapper for grammar-only elements (doc/comment) that need _get_definition."""
|
|
1099
|
+
def __init__(self, grammar_obj):
|
|
1100
|
+
self.grammar = grammar_obj
|
|
1101
|
+
self.name = None
|
|
1102
|
+
|
|
1103
|
+
def _get_definition(self, child=None):
|
|
1104
|
+
if hasattr(self.grammar, 'get_definition'):
|
|
1105
|
+
inner = self.grammar.get_definition()
|
|
1106
|
+
else:
|
|
1107
|
+
inner = {"name": type(self.grammar).__name__}
|
|
1108
|
+
return {
|
|
1109
|
+
"name": "PackageMember",
|
|
1110
|
+
"prefix": None,
|
|
1111
|
+
"ownedRelatedElement": {
|
|
1112
|
+
"name": "DefinitionElement",
|
|
1113
|
+
"ownedRelatedElement": inner
|
|
1114
|
+
}
|
|
1115
|
+
}
|