scd.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. from uuid import UUID
  2. from state.base import State
  3. from services.bottom.V0 import Bottom
  4. from services.primitives.boolean_type import Boolean
  5. from services.primitives.integer_type import Integer
  6. from services.primitives.string_type import String
  7. from services.primitives.actioncode_type import ActionCode
  8. from services import od
  9. import re
  10. class SCD:
  11. """
  12. Implements services for the simple class diagrams LTM.
  13. Implementation is done in terms of services provided by LTM-bottom
  14. """
  15. def __init__(self, model: UUID, state: State):
  16. type_model_id = state.read_dict(state.read_root(), "SCD")
  17. self.scd_model = UUID(state.read_value(type_model_id))
  18. self.model = model
  19. self.bottom = Bottom(state)
  20. def create_class(self, name: str, abstract: bool = None, min_c: int = None, max_c: int = None):
  21. """
  22. Creates an instance of a class.
  23. Args:
  24. name: the name of the class to be created
  25. abstract: indicates whether or not the class is an abstract class
  26. min_c: lower bound for class multicplicity
  27. max_c: upper bound for class multicplicity
  28. Returns:
  29. Nothing.
  30. """
  31. def set_cardinality(bound: str, value: int):
  32. """ Helper for setting cardinality attributes """
  33. # Create cardinality attribute root node
  34. # Do note that this is an instance of a ModelRef!
  35. _c_model = self.bottom.create_node()
  36. Integer(_c_model, self.bottom.state).create(value)
  37. _c_node = self.bottom.create_node(str(_c_model)) # store UUID of primitive value model
  38. self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality") # link to model root
  39. _c_link = self.bottom.create_edge(class_node, _c_node) # link class to attribute
  40. self.bottom.create_edge(self.model, _c_link, f"{name}_{bound}_cardinality") # link attr link to model root
  41. # retrieve types from metamodel
  42. _scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer")
  43. _scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Class_{bound}_cardinality")
  44. # type newly created elements
  45. self.bottom.create_edge(_c_node, _scd_node, "Morphism")
  46. self.bottom.create_edge(_c_link, _scd_link, "Morphism")
  47. # create class + attributes + morphism links
  48. class_node = self.bottom.create_node() # create class node
  49. self.bottom.create_edge(self.model, class_node, name) # attach to model
  50. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Class") # retrieve type
  51. self.bottom.create_edge(class_node, scd_node, "Morphism") # create morphism link
  52. if abstract != None:
  53. # operations similar to set_cardinality function defined above
  54. abstract_model = self.bottom.create_node()
  55. Boolean(abstract_model, self.bottom.state).create(abstract)
  56. abstract_node = self.bottom.create_node(str(abstract_model))
  57. self.bottom.create_edge(self.model, abstract_node, f"{name}.abstract")
  58. abstract_link = self.bottom.create_edge(class_node, abstract_node)
  59. self.bottom.create_edge(self.model, abstract_link, f"{name}_abstract")
  60. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean")
  61. scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "Class_abstract")
  62. self.bottom.create_edge(abstract_node, scd_node, "Morphism")
  63. self.bottom.create_edge(abstract_link, scd_link, "Morphism")
  64. if min_c != None:
  65. set_cardinality("lower", min_c)
  66. if max_c != None:
  67. set_cardinality("upper", max_c)
  68. return class_node
  69. def create_association(self, name: str, source: str, target: str,
  70. src_min_c: int = None, src_max_c: int = None,
  71. tgt_min_c: int = None, tgt_max_c: int = None):
  72. """
  73. Creates an instance of an association.
  74. Args:
  75. name: the name of the association to be created
  76. source: the name of the source of the association
  77. target: the name of the target of the association
  78. src_min_c: lower bound for source multicplicity
  79. src_max_c: upper bound for source multicplicity
  80. tgt_min_c: lower bound for target multicplicity
  81. tgt_max_c: upper bound for target multicplicity
  82. Returns:
  83. Nothing.
  84. """
  85. src, = self.bottom.read_outgoing_elements(self.model, source)
  86. tgt, = self.bottom.read_outgoing_elements(self.model, target)
  87. return self._create_association(name, src, tgt,
  88. src_min_c, src_max_c,
  89. tgt_min_c, tgt_max_c)
  90. def _create_association(self, name: str, source: UUID, target: UUID,
  91. src_min_c: int = None, src_max_c: int = None,
  92. tgt_min_c: int = None, tgt_max_c: int = None):
  93. def set_cardinality(bound: str, value: int):
  94. # similar to set_cardinality function defined in create_class
  95. _c_model = self.bottom.create_node()
  96. Integer(_c_model, self.bottom.state).create(value)
  97. _c_node = self.bottom.create_node(str(_c_model))
  98. self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality")
  99. _c_link = self.bottom.create_edge(assoc_edge, _c_node)
  100. self.bottom.create_edge(self.model, _c_link, f"{name}_{bound}_cardinality")
  101. _scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer")
  102. _scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Association_{bound}_cardinality")
  103. self.bottom.create_edge(_c_node, _scd_node, "Morphism")
  104. self.bottom.create_edge(_c_link, _scd_link, "Morphism")
  105. # create association + attributes + morphism links
  106. assoc_edge = self.bottom.create_edge(source, target) # create assoc edge
  107. self.bottom.create_edge(self.model, assoc_edge, name) # attach to model
  108. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Association") # retrieve type
  109. self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link
  110. if src_min_c != None:
  111. set_cardinality("source_lower", src_min_c)
  112. if src_max_c != None:
  113. set_cardinality("source_upper", src_max_c)
  114. if tgt_min_c != None:
  115. set_cardinality("target_lower", tgt_min_c)
  116. if tgt_max_c != None:
  117. set_cardinality("target_upper", tgt_max_c)
  118. return assoc_edge
  119. def create_global_constraint(self, name: str):
  120. """
  121. Defines a global constraint element.
  122. Args:
  123. name: the name of the global constraint to be created
  124. Returns:
  125. Nothing.
  126. """
  127. # create element + morphism links
  128. element_node = self.bottom.create_node() # create element node
  129. self.bottom.create_edge(self.model, element_node, name) # attach to model
  130. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "GlobalConstraint") # retrieve type
  131. self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link
  132. def create_attribute(self, name: str):
  133. """
  134. Defines an attribute element.
  135. Args:
  136. name: the name of the attribute to be created
  137. Returns:
  138. Nothing.
  139. """
  140. # create element + morphism links
  141. element_node = self.bottom.create_node() # create element node
  142. self.bottom.create_edge(self.model, element_node, name) # attach to model
  143. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Attribute") # retrieve type
  144. self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link
  145. def create_attribute_link(self, source: str, target: str, name: str, optional: bool):
  146. """
  147. Defines an attribute link element.
  148. Args:
  149. source: element attribute will be attached to
  150. target: attribute element
  151. name: name of the attribute
  152. optional: indicates whether attribute is optional
  153. Returns:
  154. Nothing.
  155. """
  156. tgt, = self.bottom.read_outgoing_elements(self.model, target)
  157. return self._create_attribute_link(source, tgt, name, optional)
  158. def _create_attribute_link(self, source: str, target: UUID, name: str, optional: bool):
  159. # create attribute link + morphism links
  160. src, = self.bottom.read_outgoing_elements(self.model, source)
  161. assoc_edge = self.bottom.create_edge(src, target) # create v edge
  162. self.bottom.create_edge(self.model, assoc_edge, f"{source}_{name}") # attach to model
  163. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink") # retrieve type
  164. self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link
  165. # name attribute
  166. # Do note that this is an instance of a ModelRef!
  167. name_model = self.bottom.create_node()
  168. String(name_model, self.bottom.state).create(name)
  169. name_node = self.bottom.create_node(str(name_model))
  170. self.bottom.create_edge(self.model, name_node, f"{source}_{name}.name")
  171. name_link = self.bottom.create_edge(assoc_edge, name_node)
  172. self.bottom.create_edge(self.model, name_link, f"{source}_{name}_name")
  173. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "String")
  174. scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink_name")
  175. self.bottom.create_edge(name_node, scd_node, "Morphism")
  176. self.bottom.create_edge(name_link, scd_link, "Morphism")
  177. # optional attribute
  178. # Do note that this is an instance of a ModelRef!
  179. optional_model = self.bottom.create_node()
  180. Boolean(optional_model, self.bottom.state).create(optional)
  181. optional_node = self.bottom.create_node(str(optional_model))
  182. self.bottom.create_edge(self.model, optional_node, f"{source}_{name}.optional")
  183. optional_link = self.bottom.create_edge(assoc_edge, optional_node)
  184. self.bottom.create_edge(self.model, optional_link, f"{source}_{name}_optional")
  185. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean")
  186. scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink_optional")
  187. self.bottom.create_edge(optional_node, scd_node, "Morphism")
  188. self.bottom.create_edge(optional_link, scd_link, "Morphism")
  189. return assoc_edge
  190. def create_model_ref(self, name: str, model: UUID):
  191. """
  192. Defines a model ref element.
  193. Args:
  194. name: name of the model ref
  195. model: uuid of the external model
  196. Returns:
  197. Nothing.
  198. """
  199. # create element + morphism links
  200. element_node = self.bottom.create_node(str(model)) # create element node
  201. self.bottom.create_edge(self.model, element_node, name) # attach to model
  202. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef") # retrieve type
  203. self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link
  204. return element_node
  205. def create_inheritance(self, child: str, parent: str):
  206. """
  207. Defines an inheritance link element.
  208. Args:
  209. child: child element of the inheritance relationship
  210. parent: parent element of the inheritance relationship
  211. Returns:
  212. Nothing.
  213. """
  214. c, = self.bottom.read_outgoing_elements(self.model, child)
  215. p, = self.bottom.read_outgoing_elements(self.model, parent)
  216. return self._create_inheritance(c, p)
  217. def _create_inheritance(self, child: UUID, parent: UUID):
  218. # create inheritance + morphism links
  219. inh_edge = self.bottom.create_edge(child, parent)
  220. child_name = self.get_class_name(child)
  221. parent_name = self.get_class_name(parent)
  222. self.bottom.create_edge(self.model, inh_edge, f"{child_name}_inh_{parent_name}") # attach to model
  223. scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") # retrieve type
  224. self.bottom.create_edge(inh_edge, scd_node, "Morphism") # create morphism link
  225. return inh_edge
  226. def get_class_name(self, cls: UUID):
  227. for key in self.bottom.read_keys(self.model):
  228. el, = self.bottom.read_outgoing_elements(self.model, key)
  229. if el == cls:
  230. return key
  231. def add_constraint(self, element: str, code: str):
  232. """
  233. Defines a constraint on an element.
  234. Args:
  235. element: element the constraint is attached to
  236. code: constraint code
  237. Returns:
  238. Nothing.
  239. """
  240. element_node, = self.bottom.read_outgoing_elements(self.model, element) # retrieve element
  241. # # code attribute
  242. # code_node = self.bottom.create_node(code)
  243. # self.bottom.create_edge(self.model, code_node, f"{element}.constraint")
  244. # code_link = self.bottom.create_edge(element_node, code_node)
  245. # self.bottom.create_edge(self.model, code_link, f"{element}_constraint")
  246. # scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "ActionCode")
  247. # scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "Element_constraint")
  248. # self.bottom.create_edge(code_node, scd_node, "Morphism")
  249. # self.bottom.create_edge(code_link, scd_link, "Morphism")
  250. constraint_model = self.bottom.create_node()
  251. ActionCode(constraint_model, self.bottom.state).create(code)
  252. constraint_node = self.bottom.create_node(str(constraint_model))
  253. self.bottom.create_edge(self.model, constraint_node, f"{element}.constraint")
  254. constraint_link = self.bottom.create_edge(element_node, constraint_node)
  255. self.bottom.create_edge(self.model, constraint_link, f"{element}_constraint")
  256. type_node, = self.bottom.read_outgoing_elements(self.scd_model, "ActionCode")
  257. type_link, = self.bottom.read_outgoing_elements(self.scd_model, "Element_constraint")
  258. self.bottom.create_edge(constraint_node, type_node, "Morphism")
  259. self.bottom.create_edge(constraint_link, type_link, "Morphism")
  260. def list_elements(self):
  261. """
  262. Lists elements in the model.
  263. Returns:
  264. A list of elements in alphabetical order.
  265. """
  266. scd_names = {}
  267. for key in self.bottom.read_keys(self.scd_model):
  268. element, = self.bottom.read_outgoing_elements(self.scd_model, key)
  269. scd_names[element] = key
  270. unsorted = []
  271. for key in self.bottom.read_keys(self.model):
  272. element, = self.bottom.read_outgoing_elements(self.model, key)
  273. element_types = self.bottom.read_outgoing_elements(element, "Morphism")
  274. type_model_elements = self.bottom.read_outgoing_elements(self.scd_model)
  275. element_type_node, = [e for e in element_types if e in type_model_elements]
  276. unsorted.append(f"{key} : {scd_names[element_type_node]}")
  277. return sorted(unsorted)
  278. def get_classes(self):
  279. class_node, = self.bottom.read_outgoing_elements(self.scd_model, "Class")
  280. return self.get_typed_by(class_node)
  281. def get_associations(self):
  282. assoc_node, = self.bottom.read_outgoing_elements(self.scd_model, "Association")
  283. return self.get_typed_by(assoc_node)
  284. def get_inheritances(self):
  285. inh_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance")
  286. return self.get_typed_by(inh_node)
  287. def get_typed_by(self, type_node: UUID):
  288. name_to_instance = {}
  289. for key in self.bottom.read_keys(self.model):
  290. element, = self.bottom.read_outgoing_elements(self.model, key)
  291. element_types = self.bottom.read_outgoing_elements(element, "Morphism")
  292. if type_node in element_types:
  293. name_to_instance[key] = element
  294. # mapping from instance name to UUID
  295. return name_to_instance
  296. def get_attributes(self, class_name: str):
  297. class_node, = self.bottom.read_outgoing_elements(self.model, class_name)
  298. return self._get_attributes(class_node)
  299. def _get_attributes(self, class_node: UUID):
  300. attr_link_node, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink")
  301. name_to_attr = {}
  302. for name in self.bottom.read_keys(class_node):
  303. edges = self.bottom.read_outgoing_edges(class_node, name)
  304. for edge in edges:
  305. edge_types = self.bottom.read_outgoing_elements(edge, "Morphism")
  306. if attr_link_node in edge_types:
  307. name_to_attr[name] = edge
  308. return name_to_attr
  309. def get_class_cardinalities(self, class_node):
  310. lower_card = od.find_cardinality(self.bottom, class_node, od.get_scd_mm_class_lowercard_node(self.bottom))
  311. upper_card = od.find_cardinality(self.bottom, class_node, od.get_scd_mm_class_uppercard_node(self.bottom))
  312. return lower_card, upper_card
  313. def get_assoc_cardinalities(self, assoc_edge):
  314. src_lower_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_src_lowercard_node(self.bottom))
  315. src_upper_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_src_uppercard_node(self.bottom))
  316. tgt_lower_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_tgt_lowercard_node(self.bottom))
  317. tgt_upper_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_tgt_uppercard_node(self.bottom))
  318. return src_lower_card, src_upper_card, tgt_lower_card, tgt_upper_card
  319. def delete_element(self, name: str):
  320. """
  321. Deletes an element from the model.
  322. Args:
  323. name: name of the element to delete
  324. Returns:
  325. Nothing.
  326. """
  327. keys = self.bottom.read_keys(self.model)
  328. r = re.compile(r"{}\..*".format(name))
  329. to_delete = list(filter(r.match, keys))
  330. for key in to_delete:
  331. # TODO: find way to solve memory leak, primitive models are not deleted this way
  332. node, = self.bottom.read_outgoing_elements(self.model, label=key)
  333. self.bottom.delete_element(node)
  334. def to_bottom(self):
  335. # already implemented in terms of LTM bottom
  336. pass
  337. def from_bottom(self):
  338. # already implemented in terms of LTM bottom
  339. pass
  340. if __name__ == '__main__':
  341. from state.devstate import DevState as State
  342. s = State()
  343. from bootstrap.scd import bootstrap_scd
  344. scd = bootstrap_scd(s)
  345. # Retrieve refs to primitive type models
  346. # # integer
  347. int_type_id = s.read_dict(s.read_root(), "Integer")
  348. int_type = UUID(s.read_value(int_type_id))
  349. print(f"Integer Model UUID: {int_type}") # 6
  350. # # string
  351. str_type_id = s.read_dict(s.read_root(), "String")
  352. str_type = UUID(s.read_value(str_type_id))
  353. print(f"String Model UUID: {str_type}") # 16
  354. # Create LTM_PN
  355. model_uuid = s.create_node()
  356. print(f"LTM_PN Model UUID: {model_uuid}") # 845
  357. service = SCD(model_uuid, s)
  358. # Create classes
  359. service.create_class("P")
  360. service.create_class("T")
  361. # Create associations
  362. service.create_association("P2T", "P", "T")
  363. service.create_association("T2P", "T", "P")
  364. # Create model refs
  365. service.create_model_ref("Integer", int_type)
  366. service.create_model_ref("String", int_type)
  367. # Create class attributes
  368. service.create_attribute_link("P", "Integer", "t", False)
  369. service.create_attribute_link("P", "String", "n", False)
  370. service.create_attribute_link("T", "String", "n", False)
  371. # Create association attributes
  372. service.create_attribute_link("P2T", "Integer", "w", False)
  373. service.create_attribute_link("T2P", "Integer", "w", False)