od.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. from services import od
  2. from api import cd
  3. from services.bottom.V0 import Bottom
  4. from uuid import UUID
  5. from typing import Optional
  6. NEXT_ID = 0
  7. # Models map names to elements
  8. # This builds the inverse mapping, so we can quickly lookup the name of an element
  9. def build_name_mapping(state, m):
  10. mapping = {}
  11. bottom = Bottom(state)
  12. for name in bottom.read_keys(m):
  13. element, = bottom.read_outgoing_elements(m, name)
  14. mapping[element] = name
  15. return mapping
  16. # Object Diagram API
  17. # Intended to replace the 'services.od.OD' class eventually
  18. class ODAPI:
  19. def __init__(self, state, m: UUID, mm: UUID):
  20. self.state = state
  21. self.bottom = Bottom(state)
  22. self.m = m
  23. self.mm = mm
  24. self.od = od.OD(mm, m, state)
  25. self.cd = cd.CDAPI(state, mm)
  26. self.create_boolean_value = self.od.create_boolean_value
  27. self.create_integer_value = self.od.create_integer_value
  28. self.create_string_value = self.od.create_string_value
  29. self.create_actioncode_value = self.od.create_actioncode_value
  30. self.__recompute_mappings()
  31. # Called after every change - makes querying faster but modifying slower
  32. def __recompute_mappings(self):
  33. self.obj_to_name = {**build_name_mapping(self.state, self.m), **build_name_mapping(self.state, self.mm)}
  34. # self.obj_to_type = {}
  35. self.type_to_objs = { type_name : set() for type_name in self.bottom.read_keys(self.mm)}
  36. for m_name in self.bottom.read_keys(self.m):
  37. m_element, = self.bottom.read_outgoing_elements(self.m, m_name)
  38. tm_element = self.get_type(m_element)
  39. tm_name = self.obj_to_name[tm_element]
  40. # self.obj_to_type[m_name] = tm_name
  41. self.type_to_objs[tm_name].add(m_name)
  42. def get_value(self, obj: UUID):
  43. return od.read_primitive_value(self.bottom, obj, self.mm)[0]
  44. def get_target(self, link: UUID):
  45. return self.bottom.read_edge_target(link)
  46. def get_source(self, link: UUID):
  47. return self.bottom.read_edge_source(link)
  48. def get_slot(self, obj: UUID, attr_name: str):
  49. slot = self.od.get_slot(obj, attr_name)
  50. if slot == None:
  51. raise Exception(f"Object '{self.obj_to_name[obj]}' has no slot '{attr_name}'")
  52. return slot
  53. def get_slot_link(self, obj: UUID, attr_name: str):
  54. return self.od.get_slot_link(obj, attr_name)
  55. def get_outgoing(self, obj: UUID, assoc_name: str):
  56. return od.find_outgoing_typed_by(self.bottom, src=obj, type_node=self.bottom.read_outgoing_elements(self.mm, assoc_name)[0])
  57. def get_incoming(self, obj: UUID, assoc_name: str):
  58. return od.find_incoming_typed_by(self.bottom, tgt=obj, type_node=self.bottom.read_outgoing_elements(self.mm, assoc_name)[0])
  59. def get_all_instances(self, type_name: str, include_subtypes=True):
  60. if include_subtypes:
  61. all_types = self.cd.transitive_sub_types[type_name]
  62. else:
  63. all_types = set([type_name])
  64. obj_names = [obj_name for type_name in all_types for obj_name in self.type_to_objs[type_name]]
  65. return [(obj_name, self.bottom.read_outgoing_elements(self.m, obj_name)[0]) for obj_name in obj_names]
  66. def get_type(self, obj: UUID):
  67. types = self.bottom.read_outgoing_elements(obj, "Morphism")
  68. if len(types) != 1:
  69. raise Exception(f"Expected obj to have 1 type, instead got {len(types)} types.")
  70. return types[0]
  71. def get_name(self, obj: UUID):
  72. return (
  73. [name for name in self.bottom.read_keys(self.m) if self.bottom.read_outgoing_elements(self.m, name)[0] == obj] +
  74. [name for name in self.bottom.read_keys(self.mm) if self.bottom.read_outgoing_elements(self.mm, name)[0] == obj]
  75. )[0]
  76. return self.obj_to_name[obj]
  77. def get(self, name: str):
  78. return self.bottom.read_outgoing_elements(self.m, name)[0]
  79. def get_type_name(self, obj: UUID):
  80. return self.get_name(self.get_type(obj))
  81. def is_instance(obj: UUID, type_name: str, include_subtypes=True):
  82. typ = self.cd.get_type(type_name)
  83. types = set(typ) if not include_subtypes else self.cd.transitive_sub_types[type_name]
  84. for type_of_obj in self.bottom.read_outgoing_elements(obj, "Morphism"):
  85. if type_of_obj in types:
  86. return True
  87. return False
  88. def delete(self, obj: UUID):
  89. self.bottom.delete_element(obj)
  90. self.__recompute_mappings()
  91. def has_slot(self, obj: UUID, attr_name: str):
  92. class_name = self.get_name(self.get_type(obj))
  93. return self.od.get_attr_link_name(class_name, attr_name) != None
  94. def get_slot_value(self, obj: UUID, attr_name: str):
  95. return self.get_value(self.get_slot(obj, attr_name))
  96. # create or update slot value
  97. def set_slot_value(self, obj: UUID, attr_name: str, new_value: any):
  98. obj_name = self.get_name(obj)
  99. link_name = f"{obj_name}_{attr_name}"
  100. target_name = f"{obj_name}.{attr_name}"
  101. old_slot_link = self.get_slot_link(obj, attr_name)
  102. if old_slot_link != None:
  103. old_target = self.get_target(old_slot_link)
  104. # if old_target != None:
  105. self.bottom.delete_element(old_target) # this also deletes the slot-link
  106. new_target = self.create_primitive_value(target_name, new_value)
  107. slot_type = self.cd.find_attribute_type(self.get_type_name(obj), attr_name)
  108. new_link = self.od._create_link(link_name, slot_type, obj, new_target)
  109. self.__recompute_mappings()
  110. def create_primitive_value(self, name: str, value: any, is_code=False):
  111. if isinstance(value, bool):
  112. tgt = self.create_boolean_value(name, value)
  113. elif isinstance(value, int):
  114. tgt = self.create_integer_value(name, value)
  115. elif isinstance(value, str):
  116. if is_code:
  117. tgt = self.create_actioncode_value(name, value)
  118. else:
  119. tgt = self.create_string_value(name, value)
  120. else:
  121. raise Exception("Unimplemented type "+value)
  122. self.__recompute_mappings()
  123. return tgt
  124. def create_link(self, link_name: Optional[str], assoc_name: str, src: UUID, tgt: UUID):
  125. global NEXT_ID
  126. typ, = self.bottom.read_outgoing_elements(self.mm, assoc_name)
  127. if link_name == None:
  128. link_name = f"__{assoc_name}{NEXT_ID}"
  129. NEXT_ID += 1
  130. link_id = self.od._create_link(link_name, typ, src, tgt)
  131. self.__recompute_mappings()
  132. return link_id
  133. def create_object(self, object_name: Optional[str], class_name: str):
  134. return self.od.create_object(object_name, class_name)