merger.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. from api.od import ODAPI
  2. from uuid import UUID
  3. from concrete_syntax.textual_od import parser, renderer
  4. from services.scd import SCD
  5. from util.timer import Timer
  6. PRIMITIVE_TYPES = set(["Integer", "String", "Boolean", "ActionCode"])
  7. # Merges N models. The models must have the same meta-model.
  8. # Care should be taken to avoid naming collisions before calling this function.
  9. def merge_models(state, mm, models: list[UUID]):
  10. with Timer("merge_models"):
  11. primitive_types = {
  12. type_name : UUID(state.read_value(state.read_dict(state.read_root(), type_name)))
  13. for type_name in ["Integer", "String", "Boolean", "ActionCode"]
  14. }
  15. merged = state.create_node()
  16. merged_odapi = ODAPI(state, m=merged, mm=mm)
  17. scd_mmm = UUID(state.read_value(state.read_dict(state.read_root(), "SCD")))
  18. mm_odapi = ODAPI(state, m=mm, mm=scd_mmm)
  19. types = mm_odapi.get_all_instances("Class", include_subtypes=True)
  20. all_objs = []
  21. for type_name, type_obj in types:
  22. for model in models:
  23. m_odapi = ODAPI(state, m=model, mm=mm)
  24. for obj_name, obj in m_odapi.get_all_instances(type_name, include_subtypes=False):
  25. all_objs.append((obj_name, obj, type_name, m_odapi))
  26. todo = all_objs
  27. have = {}
  28. mapping = {}
  29. while len(todo) > 0:
  30. next_round = []
  31. # if 'mm' is SCD, class_name will be 'Class', 'Association', ...
  32. for tup in todo:
  33. obj_name, obj, type_name, m_odapi = tup
  34. prefixed_obj_name = obj_name
  35. if obj_name in PRIMITIVE_TYPES:
  36. if prefixed_obj_name in have:
  37. # Don't rename primitive types. Instead, merge them.
  38. mapping[obj] = mapping[have[prefixed_obj_name]]
  39. continue
  40. while prefixed_obj_name in have:
  41. prefixed_obj_name = prefixed_obj_name + '_bis' # make name unique
  42. if prefixed_obj_name != obj_name:
  43. print(f"Warning: renaming {obj_name} to {prefixed_obj_name} to avoid naming collision.")
  44. if type_name == "ModelRef":
  45. model = state.read_value(obj)
  46. scd = SCD(merged, state)
  47. created_obj = scd.create_model_ref(prefixed_obj_name, model)
  48. merged_odapi._ODAPI__recompute_mappings() # dirty!!
  49. else:
  50. # create node or edge
  51. if state.is_edge(obj):
  52. source, target = state.read_edge(obj)
  53. if source not in mapping or target not in mapping:
  54. next_round.append(tup)
  55. continue # try again later...
  56. else:
  57. created_obj = merged_odapi.create_link(prefixed_obj_name, type_name, mapping[source], mapping[target])
  58. else:
  59. created_obj = merged_odapi.create_object(prefixed_obj_name, type_name)
  60. mapping[obj] = created_obj
  61. have[obj_name] = obj
  62. # copy slots
  63. for attr_name in m_odapi.get_slots(obj):
  64. value = m_odapi.get_slot_value(obj, attr_name)
  65. is_code = m_odapi.slot_has_code(obj, attr_name)
  66. merged_odapi.set_slot_value(created_obj, attr_name, value, is_code=is_code)
  67. if len(next_round) == len(todo):
  68. raise Exception("We got stuck!")
  69. todo = next_round
  70. return merged